Compare commits
1
Commits
v0.1.6
..
2a91f9f211
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a91f9f211 |
@@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug Report
|
|
||||||
about: Неверное поведение
|
|
||||||
---
|
|
||||||
# Bug Report
|
|
||||||
|
|
||||||
## Проблема и шаги воспроизведения
|
|
||||||
|
|
||||||
## Факт / ожидание
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- [ ]
|
|
||||||
|
|
||||||
## Окружение / ссылки (если применимо)
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
---
|
|
||||||
name: Engineering Task
|
|
||||||
about: CI, тесты, документация, процесс и прочее
|
|
||||||
---
|
|
||||||
# Engineering Task
|
|
||||||
|
|
||||||
## Контекст и результат
|
|
||||||
|
|
||||||
## Scope / связанные Issues (если есть)
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- [ ]
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature Request
|
|
||||||
about: Новое наблюдаемое поведение
|
|
||||||
---
|
|
||||||
# Feature Request
|
|
||||||
|
|
||||||
## Зачем и какой результат
|
|
||||||
|
|
||||||
## Ожидаемое поведение
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- [ ]
|
|
||||||
|
|
||||||
## Зависимости / связанные Issues (если есть)
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
name: Refactoring / Technical Debt
|
|
||||||
about: Улучшение без нового поведения
|
|
||||||
---
|
|
||||||
# Refactoring / Technical Debt
|
|
||||||
|
|
||||||
## Проблема и результат
|
|
||||||
|
|
||||||
## Сохраняемые инварианты / границы
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- [ ]
|
|
||||||
|
|
||||||
## Зависимости / ссылки (если есть)
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
# Pull Request
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
Краткий результат работы описывается здесь.
|
|
||||||
|
|
||||||
## Linked Issue
|
|
||||||
|
|
||||||
Refs `<owner/repo#number>` (заменить ссылкой на реальную Issue).
|
|
||||||
|
|
||||||
## Changes
|
|
||||||
|
|
||||||
Затронутые части и изменения.
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
- Команда / результат / ограничения:
|
|
||||||
|
|
||||||
## Breaking Changes (если есть)
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
name: Conventional commits
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened, edited]
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: docs-commits-${{ gitea.event.pull_request.number }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
commits:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: https://github.com/actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
|
||||||
fetch-depth: 0
|
|
||||||
- uses: https://github.com/actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: .nvmrc
|
|
||||||
- run: npm ci --ignore-scripts
|
|
||||||
- name: Check PR title and commits
|
|
||||||
env:
|
|
||||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
||||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
||||||
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
||||||
run: |
|
|
||||||
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 "$HEAD_SHA^" --to "$HEAD_SHA"
|
|
||||||
else
|
|
||||||
npm run commitlint -- --strict --from "$BASE_SHA" --to "$HEAD_SHA"
|
|
||||||
fi
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
name: Docs security
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [develop, main]
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
schedule:
|
|
||||||
- cron: '17 5 * * *'
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: docs-security-${{ gitea.event_name }}-${{ gitea.event.pull_request.number || gitea.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
security:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: https://github.com/actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Record checked revision
|
|
||||||
run: git rev-parse HEAD
|
|
||||||
- name: Scan history with Gitleaks 8.30.1
|
|
||||||
run: |
|
|
||||||
curl --fail --location --silent --show-error https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz -o /tmp/gitleaks.tar.gz
|
|
||||||
echo '551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb /tmp/gitleaks.tar.gz' | sha256sum --check
|
|
||||||
mkdir -p /tmp/gitleaks-bin
|
|
||||||
tar -xzf /tmp/gitleaks.tar.gz -C /tmp/gitleaks-bin gitleaks
|
|
||||||
/tmp/gitleaks-bin/gitleaks git --log-opts=--all --redact=100 --no-banner
|
|
||||||
- uses: https://github.com/actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: .nvmrc
|
|
||||||
- name: Audit runtime and development dependencies
|
|
||||||
run: npm audit --include=dev --audit-level=low
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
name: Docs verify
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [develop, main]
|
|
||||||
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:
|
|
||||||
verify:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: https://github.com/actions/checkout@v4
|
|
||||||
- name: Record checked revision
|
|
||||||
run: git rev-parse HEAD
|
|
||||||
- uses: https://github.com/actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: .nvmrc
|
|
||||||
- run: npm ci
|
|
||||||
- name: Check Markdown and relative links
|
|
||||||
run: npm run verify
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": ".markdownlint.json",
|
|
||||||
"MD024": {
|
|
||||||
"siblings_only": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"MD013": false
|
||||||
|
},
|
||||||
|
"customRules": ["markdownlint-rule-relative-links"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
// Historical evidence records keep original source paths, outside the standalone book.
|
||||||
|
"filter": ["development/completion-evidence.md", "development/audit-remediation.md"],
|
||||||
|
"config": { "relative-links": false },
|
||||||
|
"combine": "merge"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
module.exports = require('markdownlint-rule-relative-links').default;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"MD013": false
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
{
|
|
||||||
"git": {
|
|
||||||
"requireBranch": "release/*",
|
|
||||||
"requireCleanWorkingDir": true,
|
|
||||||
"requireUpstream": false,
|
|
||||||
"requireCommits": true,
|
|
||||||
"commitMessage": "chore(release): ${version}",
|
|
||||||
"tagName": "v${version}",
|
|
||||||
"tag": false,
|
|
||||||
"push": false
|
|
||||||
},
|
|
||||||
"npm": {
|
|
||||||
"publish": false
|
|
||||||
},
|
|
||||||
"github": {
|
|
||||||
"release": false
|
|
||||||
},
|
|
||||||
"gitlab": {
|
|
||||||
"release": false
|
|
||||||
},
|
|
||||||
"hooks": {
|
|
||||||
"after:git:init": "node scripts/check-release.cjs"
|
|
||||||
},
|
|
||||||
"plugins": {
|
|
||||||
"@release-it/conventional-changelog": {
|
|
||||||
"infile": "CHANGELOG.md",
|
|
||||||
"preset": {
|
|
||||||
"name": "conventionalcommits",
|
|
||||||
"types": [
|
|
||||||
{
|
|
||||||
"type": "chore",
|
|
||||||
"scope": "release",
|
|
||||||
"effect": "hidden"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "feat",
|
|
||||||
"section": "Features",
|
|
||||||
"effect": "bump"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "fix",
|
|
||||||
"section": "Bug Fixes",
|
|
||||||
"effect": "bump"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "perf",
|
|
||||||
"section": "Performance",
|
|
||||||
"effect": "bump"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "docs",
|
|
||||||
"section": "Documentation",
|
|
||||||
"effect": "bump"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "spec",
|
|
||||||
"section": "Specifications",
|
|
||||||
"effect": "bump"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "refactor",
|
|
||||||
"section": "Refactoring",
|
|
||||||
"effect": "bump"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "build",
|
|
||||||
"section": "Build",
|
|
||||||
"effect": "bump"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "ci",
|
|
||||||
"section": "CI",
|
|
||||||
"effect": "bump"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "test",
|
|
||||||
"section": "Tests",
|
|
||||||
"effect": "bump"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "style",
|
|
||||||
"section": "Styles",
|
|
||||||
"effect": "bump"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "chore",
|
|
||||||
"section": "Maintenance",
|
|
||||||
"effect": "bump"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "revert",
|
|
||||||
"section": "Reverts",
|
|
||||||
"effect": "bump"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"context": {
|
|
||||||
"host": "https://git.personal.ksv741.ru",
|
|
||||||
"owner": "WealthLedger",
|
|
||||||
"repository": "docs",
|
|
||||||
"linkReferences": true,
|
|
||||||
"commit": "commit"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,16 +3,11 @@
|
|||||||
Перед изменением прочитай [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 и реализации с учётом
|
||||||
внешние источники используй как справочные свидетельства, когда они доступны.
|
веток и незакоммиченных изменений. Расхождение обозначь, не выбирая источник
|
||||||
Недостающие факты отмечай явно; инструкции и проверки не требуют других checkout.
|
|
||||||
Расхождение обозначь, не выбирая источник
|
|
||||||
победителем автоматически. Перенос сохраняет полезное содержание и входящие
|
победителем автоматически. Перенос сохраняет полезное содержание и входящие
|
||||||
ссылки. Docs описывает текущее устройство системы; история поставок остаётся в Git.
|
ссылки; существующие исторические свидетельства остаются неизменными.
|
||||||
Отчёты аудитов и исследований в docs не размещаются. Подтверждённые решения
|
Отчёты аудитов и исследований в docs не размещаются. Подтверждённые решения
|
||||||
из исследований оформляются в ADR, системные знания — в тематических страницах.
|
из исследований оформляются в ADR, системные знания — в тематических страницах.
|
||||||
|
|
||||||
@@ -20,16 +15,3 @@
|
|||||||
[доступные проверки](CONTRIBUTING.md#проверки), включая новые файлы.
|
[доступные проверки](CONTRIBUTING.md#проверки), включая новые файлы.
|
||||||
Сохрани границы задачи и чужие изменения. Успех Markdown-проверки не заменяет
|
Сохрани границы задачи и чужие изменения. Успех Markdown-проверки не заменяет
|
||||||
смысловую сверку и не доказывает рендер диаграмм.
|
смысловую сверку и не доказывает рендер диаграмм.
|
||||||
|
|
||||||
## Ветки и релизы
|
|
||||||
|
|
||||||
Работа — в issue-linked ветке от `develop`, затем PR в `develop`; в `main` напрямую
|
|
||||||
не пушить. PR сливается squash или rebase-merge; merge-коммит не используется.
|
|
||||||
`main` обновляется только fast-forward из `develop`, поэтому `origin/main`
|
|
||||||
всегда предок `origin/develop`:
|
|
||||||
`git merge-base --is-ancestor origin/main origin/develop`. Срочный fix —
|
|
||||||
обычная ветка `fix/*` от `develop`; пути hotfix от `main` нет.
|
|
||||||
Релиз — по [README](README.md#релизы).
|
|
||||||
Commits и заголовки PR — Conventional Commits со ссылкой на Issue (кроме release);
|
|
||||||
для спецификаций допустим `spec:`.
|
|
||||||
Перед подготовкой релиза прочитай местный README; версии вручную не повышать.
|
|
||||||
|
|||||||
-202
@@ -1,202 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
## [0.1.6](https://git.personal.ksv741.ru/WealthLedger/docs/compare/v0.1.5...v0.1.6) (2026-10-24)
|
|
||||||
|
|
||||||
### Maintenance
|
|
||||||
|
|
||||||
* bump Node to 24.22.1 (WealthLedger/backend[#122](https://git.personal.ksv741.ru/WealthLedger/docs/issues/122)) ([4216eb8](https://git.personal.ksv741.ru/WealthLedger/docs/commit/4216eb815f59b7a284e679ccb1b725438da07882))
|
|
||||||
* **deps:** update non-major dependencies (WealthLedger/docs[#56](https://git.personal.ksv741.ru/WealthLedger/docs/issues/56)) ([f9098d8](https://git.personal.ksv741.ru/WealthLedger/docs/commit/f9098d84098467e41143bad5044085d6329099d9))
|
|
||||||
|
|
||||||
## [0.1.5](https://git.personal.ksv741.ru/WealthLedger/docs/compare/v0.1.4...v0.1.5) (2026-10-18)
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
|
|
||||||
* align current pages with hourly freshness model (WealthLedger/docs[#34](https://git.personal.ksv741.ru/WealthLedger/docs/issues/34)) ([#42](https://git.personal.ksv741.ru/WealthLedger/docs/issues/42)) ([7b61eb8](https://git.personal.ksv741.ru/WealthLedger/docs/commit/7b61eb8e6ba93a765b1ae27ca31d77b5e422e149))
|
|
||||||
* describe ASCII 8-72 password policy and drop resolved discrepancy (WealthLedger/docs[#46](https://git.personal.ksv741.ru/WealthLedger/docs/issues/46)) ([#47](https://git.personal.ksv741.ru/WealthLedger/docs/issues/47)) ([56885f4](https://git.personal.ksv741.ru/WealthLedger/docs/commit/56885f450bc1298d383d496174d16e591926e047))
|
|
||||||
* describe events default period and picker bounds (WealthLedger/frontend[#63](https://git.personal.ksv741.ru/WealthLedger/docs/issues/63)) ([8a2a62c](https://git.personal.ksv741.ru/WealthLedger/docs/commit/8a2a62c611e1acc1c1e69048a88b049bbf0d9087))
|
|
||||||
* describe quiet reading and visible source state ([#51](https://git.personal.ksv741.ru/WealthLedger/docs/issues/51)) ([e8e3ef6](https://git.personal.ksv741.ru/WealthLedger/docs/commit/e8e3ef62854dae8448deaa66ac26e5b15d5c7d6a))
|
|
||||||
* document income without comparisons and unified payouts (WealthLedger/main[#33](https://git.personal.ksv741.ru/WealthLedger/docs/issues/33)) ([#40](https://git.personal.ksv741.ru/WealthLedger/docs/issues/40)) ([03fb045](https://git.personal.ksv741.ru/WealthLedger/docs/commit/03fb045a9ee8a36775599bda66e7f6a08362bd84))
|
|
||||||
* document Nexus delivery with shared account (WealthLedger/docs[#18](https://git.personal.ksv741.ru/WealthLedger/docs/issues/18)) ([#48](https://git.personal.ksv741.ru/WealthLedger/docs/issues/48)) ([550143a](https://git.personal.ksv741.ru/WealthLedger/docs/commit/550143aea1f0ac90630f326dd927091b0ec4f38e))
|
|
||||||
|
|
||||||
### CI
|
|
||||||
|
|
||||||
* rerun commits proof on PR title edit (WealthLedger/docs[#35](https://git.personal.ksv741.ru/WealthLedger/docs/issues/35)) ([03110e9](https://git.personal.ksv741.ru/WealthLedger/docs/commit/03110e99ea58600bacd4c091fda8591d374a5a8d))
|
|
||||||
|
|
||||||
### Maintenance
|
|
||||||
|
|
||||||
* adopt linear git model in docs instructions ([#49](https://git.personal.ksv741.ru/WealthLedger/docs/issues/49)) ([11f819e](https://git.personal.ksv741.ru/WealthLedger/docs/commit/11f819ed31226a05524ac35a893064525713e41b))
|
|
||||||
|
|
||||||
## [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)
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
|
|
||||||
* describe local frontend packages and ownership ([55610ea](https://git.personal.ksv741.ru/WealthLedger/docs/commit/55610ea2b4dc687f76ee9eb37c14d887b1b70100))
|
|
||||||
* document Nexus registry migration [#18](https://git.personal.ksv741.ru/WealthLedger/docs/issues/18) ([5de85c2](https://git.personal.ksv741.ru/WealthLedger/docs/commit/5de85c296f7681882f58476343d3da71f1dee28d))
|
|
||||||
* document versioned deployment bundle ([15d1d82](https://git.personal.ksv741.ru/WealthLedger/docs/commit/15d1d820f5401c25adda23f753b99e894bdcd4d3))
|
|
||||||
* explain persisted T-Bank reads and synchronization ([07de062](https://git.personal.ksv741.ru/WealthLedger/docs/commit/07de062ebbf043eff50bd7f53b48bff13bb7cb1b))
|
|
||||||
* use shared Nexus account [#18](https://git.personal.ksv741.ru/WealthLedger/docs/issues/18) ([6fd39cd](https://git.personal.ksv741.ru/WealthLedger/docs/commit/6fd39cd467566e23cca79a1235125dfb80fa99f7))
|
|
||||||
|
|
||||||
## [0.1.1](https://git.personal.ksv741.ru/WealthLedger/docs/compare/v0.1.0...v0.1.1) (2026-09-16)
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
|
|
||||||
* document container deployment and recovery ([cead9dc](https://git.personal.ksv741.ru/WealthLedger/docs/commit/cead9dc1765700b7e10f9567e38f6d68d4ec9656))
|
|
||||||
* make documentation checks self-contained ([97069d6](https://git.personal.ksv741.ru/WealthLedger/docs/commit/97069d6f6d0719d6fcee30b1ce8adbda41bd3a6d))
|
|
||||||
|
|
||||||
## 0.1.0 (2026-09-14)
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* add authorization ([6ca175b](https://git.personal.ksv741.ru/WealthLedger/docs/commit/6ca175b541a9f03e08448bef7593e560e928a15c))
|
|
||||||
* add brocker account list ([ebf34e0](https://git.personal.ksv741.ru/WealthLedger/docs/commit/ebf34e01ca8c8dab533bb76aff3d5b03177383f8))
|
|
||||||
* add logging ([016b624](https://git.personal.ksv741.ru/WealthLedger/docs/commit/016b624185d24a4e5ff07d4d164f2a3942b12358))
|
|
||||||
* add operations list ([d825126](https://git.personal.ksv741.ru/WealthLedger/docs/commit/d8251269207b6f163889f04637862d9126cd89ca))
|
|
||||||
* add portfolio ([4ffc374](https://git.personal.ksv741.ru/WealthLedger/docs/commit/4ffc374bd8ae4cca5a05ac806bf8d9a423d1153f))
|
|
||||||
* add tbank tokens ([026b531](https://git.personal.ksv741.ru/WealthLedger/docs/commit/026b5318af0d1d2b8e4622fc82cc454c23222bd8))
|
|
||||||
* add агегку events ([c513c47](https://git.personal.ksv741.ru/WealthLedger/docs/commit/c513c478404c6d00475ad093f70fce54aa30d1c2))
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
|
|
||||||
* activate frontend quality guardrails remediation ([fa5db07](https://git.personal.ksv741.ru/WealthLedger/docs/commit/fa5db0722915dd21fff49dbf49b8ddfc42eb4733))
|
|
||||||
* activate token-efficient agent workflow ([42fd894](https://git.personal.ksv741.ru/WealthLedger/docs/commit/42fd8940dcd2fcfba5ed7f34d6265f289b049659))
|
|
||||||
* add account income analytics ([cc3b5b0](https://git.personal.ksv741.ru/WealthLedger/docs/commit/cc3b5b0ae5b0e9507e2267c47fb55ea969f2765b))
|
|
||||||
* add audit remediation registry ([793d201](https://git.personal.ksv741.ru/WealthLedger/docs/commit/793d2012a39e0b204d1dc768c386e60cb987b05a))
|
|
||||||
* add baseline design ([e610c83](https://git.personal.ksv741.ru/WealthLedger/docs/commit/e610c83366045e5e8dab71ea5edbe7796bb78364))
|
|
||||||
* add completion evidence for extend-instrument-logo-to-bonds ([c68c978](https://git.personal.ksv741.ru/WealthLedger/docs/commit/c68c978c91d3fc821b8a31a9e52c7406a7121bb6))
|
|
||||||
* add old audit file ([5bf5c4e](https://git.personal.ksv741.ru/WealthLedger/docs/commit/5bf5c4e774f3dae8610a8ea9cba1204b1f3532ae))
|
|
||||||
* **ai:** adopt task-driven scoped context ([b7a56b1](https://git.personal.ksv741.ru/WealthLedger/docs/commit/b7a56b1eb53bae0a00e18be4ed4209df70fc193b))
|
|
||||||
* align investment domain terminology ([68d975b](https://git.personal.ksv741.ru/WealthLedger/docs/commit/68d975befda90c198a0287bb8d43ac5d6e386eb5))
|
|
||||||
* **brokerage-account-ledger:** describe explicit filters ([820cf55](https://git.personal.ksv741.ru/WealthLedger/docs/commit/820cf5510318e0868eba46a6ff0b556053cb9806))
|
|
||||||
* **brokerage-account-ledger:** describe operation type label and no category duplication ([2abb591](https://git.personal.ksv741.ru/WealthLedger/docs/commit/2abb5912b2db23d4984ca3b99578310596d66555))
|
|
||||||
* **cards:** describe compact expense summaries ([d3a9e91](https://git.personal.ksv741.ru/WealthLedger/docs/commit/d3a9e91d475414f92b01f53848b58bd20263a56b))
|
|
||||||
* **cards:** describe date range filtering ([a86d1eb](https://git.personal.ksv741.ru/WealthLedger/docs/commit/a86d1eb0a3d34992e650075c9f94cec1bda252e8))
|
|
||||||
* clarify domain and agent authority ([0c53c50](https://git.personal.ksv741.ru/WealthLedger/docs/commit/0c53c50c624c9ab8d9c812c60a7cab56661fcbbd))
|
|
||||||
* close SQLite migration and e2e blocker ([c74c573](https://git.personal.ksv741.ru/WealthLedger/docs/commit/c74c57373329e4accc46dbc13fdbe57ebf7ea3fd))
|
|
||||||
* describe all-accounts behavior ([0e8c8c8](https://git.personal.ksv741.ru/WealthLedger/docs/commit/0e8c8c803f8ce8b49cdb7cbf2d5d1b0018736ee9))
|
|
||||||
* describe bank operation icons and date format ([1f65b91](https://git.personal.ksv741.ru/WealthLedger/docs/commit/1f65b91a16a3ab90de17a493d011a8cf3fdc3c1c))
|
|
||||||
* describe bond pages and rating sources ([35711a5](https://git.personal.ksv741.ru/WealthLedger/docs/commit/35711a52ec79d05887b39adb5a64b52578b4da21))
|
|
||||||
* describe brokerage account summaries ([76d4de4](https://git.personal.ksv741.ru/WealthLedger/docs/commit/76d4de4428e5f902d82782ade745487d015994a1))
|
|
||||||
* describe compact portfolio totals ([2985def](https://git.personal.ksv741.ru/WealthLedger/docs/commit/2985def574f1c22f9c41f6755b802342fafb30cd))
|
|
||||||
* describe direct local token adaptation ([b0e747f](https://git.personal.ksv741.ru/WealthLedger/docs/commit/b0e747fdee2f726f18a4222f4bfc249c8f6d845b))
|
|
||||||
* describe improved ledger event rows ([d39a9d3](https://git.personal.ksv741.ru/WealthLedger/docs/commit/d39a9d3df08d068b0c5fba97cfb7bb0d5755f329))
|
|
||||||
* describe manual bank card accounting ([c0dfb80](https://git.personal.ksv741.ru/WealthLedger/docs/commit/c0dfb8061721bfd48d16e2a978348be4bec38c50))
|
|
||||||
* describe polished stock cards ([0c59ce7](https://git.personal.ksv741.ru/WealthLedger/docs/commit/0c59ce788690ba6d17f33e0b57e122dd67e74904))
|
|
||||||
* describe portable design token contract ([436251e](https://git.personal.ksv741.ru/WealthLedger/docs/commit/436251e83e07fe9726a1576dd081d5bd41655ff9))
|
|
||||||
* describe shared design system and catalog blocks ([30683b8](https://git.personal.ksv741.ru/WealthLedger/docs/commit/30683b8b73cdad838feac6c1cac255aea3783ae4))
|
|
||||||
* describe simplified income page ([7166ee5](https://git.personal.ksv741.ru/WealthLedger/docs/commit/7166ee51d181b04f3892ff6757a56b578ee9df64))
|
|
||||||
* describe stock pages and portfolio navigation ([ead965a](https://git.personal.ksv741.ru/WealthLedger/docs/commit/ead965a67402789245c267a4c32b52176d3df7a2))
|
|
||||||
* describe year-over-year income comparison ([2c98989](https://git.personal.ksv741.ru/WealthLedger/docs/commit/2c98989f5eeafd9baca8dab2659619473b78702d))
|
|
||||||
* document auth and client refresh resilience ([b91280c](https://git.personal.ksv741.ru/WealthLedger/docs/commit/b91280cbaace4d0ed6d66186c62465abeeda6032))
|
|
||||||
* document frontend audit remediation delivery ([55d35c7](https://git.personal.ksv741.ru/WealthLedger/docs/commit/55d35c7f789478230c683a79e5c80563c75487b6))
|
|
||||||
* document instrument logos in portfolio ([30e5699](https://git.personal.ksv741.ru/WealthLedger/docs/commit/30e56997f9bbceee09ea87b218a6042ab17112f0))
|
|
||||||
* document local verification workflow ([293df9c](https://git.personal.ksv741.ru/WealthLedger/docs/commit/293df9c59dfb8788e2c7e044c8855146b3c5d03e))
|
|
||||||
* document OpenSpec authority lifecycle ([0bc8d1c](https://git.personal.ksv741.ru/WealthLedger/docs/commit/0bc8d1c9aed984cc16b6efd1dab76e2a7a9ca307))
|
|
||||||
* document skeleton loading states ([95fa815](https://git.personal.ksv741.ru/WealthLedger/docs/commit/95fa8153286d549f7a58229bd92ef327b75b7e58))
|
|
||||||
* document T-Bank cache generation boundary ([3d2bc1e](https://git.personal.ksv741.ru/WealthLedger/docs/commit/3d2bc1eac4add7cc3316c97d4d02a78fe91620d5))
|
|
||||||
* establish architecture records ([2a09ef7](https://git.personal.ksv741.ru/WealthLedger/docs/commit/2a09ef74bb25feca65efdace61c2686199f614a5))
|
|
||||||
* **events:** describe date range filtering ([aa9afc1](https://git.personal.ksv741.ru/WealthLedger/docs/commit/aa9afc15e46888c4d6cabbe8e17ff87e383f5731))
|
|
||||||
* explain server-side portfolio metadata recovery ([05bfe8d](https://git.personal.ksv741.ru/WealthLedger/docs/commit/05bfe8df1cb0fde9b97d2075d9f571198295fea1))
|
|
||||||
* link remediation evidence commit ([9752234](https://git.personal.ksv741.ru/WealthLedger/docs/commit/9752234f23c8dd400667ba354b0ed1eec248db99))
|
|
||||||
* make project documentation self-contained ([3a99af1](https://git.personal.ksv741.ru/WealthLedger/docs/commit/3a99af1a6e49836ae6ea2378dd2e4037092d08fc))
|
|
||||||
* mark frontend audit remediation active ([ba15e52](https://git.personal.ksv741.ru/WealthLedger/docs/commit/ba15e529b44b39f819c3666ad4787f2f566d1da4))
|
|
||||||
* **portfolio-view:** update as-built for bond parity ([2e9e403](https://git.personal.ksv741.ru/WealthLedger/docs/commit/2e9e403db207c5fc4a21827b118407a51c7a70cc))
|
|
||||||
* **portfolio:** describe asset allocation ([174bab3](https://git.personal.ksv741.ru/WealthLedger/docs/commit/174bab3566be2d302b51a8a86e10b11271f9835f))
|
|
||||||
* **portfolio:** document ticker display ([7f4d0b2](https://git.personal.ksv741.ru/WealthLedger/docs/commit/7f4d0b255eb4ef5a73139bfd499eb88118105a6d))
|
|
||||||
* publish token workflow pilot result ([21ccad3](https://git.personal.ksv741.ru/WealthLedger/docs/commit/21ccad3639714bcc364af329ea884eda57259aab))
|
|
||||||
* record account-ledger-explicit-filters evidence ([2bc56db](https://git.personal.ksv741.ru/WealthLedger/docs/commit/2bc56db68076b243dccaa36ef850e4b28823e2fc))
|
|
||||||
* record add-instrument-logos completion evidence ([d2a98f3](https://git.personal.ksv741.ru/WealthLedger/docs/commit/d2a98f31577cdd0730efa6fad73e132572e017f2))
|
|
||||||
* record AI governance audit ([1944f2f](https://git.personal.ksv741.ru/WealthLedger/docs/commit/1944f2fc970db2bdc292981e01b1aca549f9ac52))
|
|
||||||
* record api contract resilience remediation ([b623805](https://git.personal.ksv741.ru/WealthLedger/docs/commit/b623805cbe9cd8c57cfc83a35b6c8af3018fea1e))
|
|
||||||
* record auth and cache concurrency policy ([8568e7b](https://git.personal.ksv741.ru/WealthLedger/docs/commit/8568e7b317a3c96c1e4ca2cbabce41d57a11181e))
|
|
||||||
* record auth cache remediation evidence ([6c37e58](https://git.personal.ksv741.ru/WealthLedger/docs/commit/6c37e58fbe670ef79ec774280171bc8b28d88737))
|
|
||||||
* record backend refactor and close P1-12 ([b940d39](https://git.personal.ksv741.ru/WealthLedger/docs/commit/b940d39946baae78ab14a10b9f71ef15153e9604))
|
|
||||||
* record backend strict typing completion ([f4cefd5](https://git.personal.ksv741.ru/WealthLedger/docs/commit/f4cefd5c401d009e1d3332e76f30913c97a480b7))
|
|
||||||
* record dependency hygiene remediation evidence ([60a1c33](https://git.personal.ksv741.ru/WealthLedger/docs/commit/60a1c33003bd35f7ff71e8c4b8f30d6a6f511e12))
|
|
||||||
* record documentation remediation evidence ([b91dfe6](https://git.personal.ksv741.ru/WealthLedger/docs/commit/b91dfe66e0d8c55c9000687895dfc2954f4d1c44))
|
|
||||||
* record domain money contract evidence ([4dbff85](https://git.personal.ksv741.ru/WealthLedger/docs/commit/4dbff85654656473dfa0b68f69b7841ac27d8efe))
|
|
||||||
* record frontend audit remediation evidence ([8721e93](https://git.personal.ksv741.ru/WealthLedger/docs/commit/8721e93849f72413ca065f9e738948f0ca10132e))
|
|
||||||
* record frontend auth query gating evidence ([68f1d8f](https://git.personal.ksv741.ru/WealthLedger/docs/commit/68f1d8f398e6606fb2a0db694c452ba235a29434))
|
|
||||||
* record frontend quality guardrails evidence ([eb69300](https://git.personal.ksv741.ru/WealthLedger/docs/commit/eb69300ebed41bc19e78909195387dce4e2c0ec7))
|
|
||||||
* record frontend remediation completion ([4cc8565](https://git.personal.ksv741.ru/WealthLedger/docs/commit/4cc856552498821f0dbaaa192b0fcc9a45d7b097))
|
|
||||||
* record frontend remediation evidence ([ef2bf97](https://git.personal.ksv741.ru/WealthLedger/docs/commit/ef2bf977bf6fd55580c878b0b3aec3e37a3d944e))
|
|
||||||
* record frontend view model boundary evidence ([373c18a](https://git.personal.ksv741.ru/WealthLedger/docs/commit/373c18a2f170f6ae2df1bda0b294b37e4c31d18f))
|
|
||||||
* record governance completion evidence ([1a214dd](https://git.personal.ksv741.ru/WealthLedger/docs/commit/1a214dd7649cc34ff86ec1483e68d099b69ff2e7))
|
|
||||||
* record improved ledger row evidence ([35c5302](https://git.personal.ksv741.ru/WealthLedger/docs/commit/35c5302e78545170ded1f9aa9109e3faa0ebea2a))
|
|
||||||
* record income analytics completion evidence ([09f5f9e](https://git.personal.ksv741.ru/WealthLedger/docs/commit/09f5f9e4f9ac8709e1fa66b24e485deecf5f0655))
|
|
||||||
* record ledger entry type label evidence ([d749bf5](https://git.personal.ksv741.ru/WealthLedger/docs/commit/d749bf5b2829b3127d6b8f6129edd262db37348f))
|
|
||||||
* record local verification remediation evidence ([82159e3](https://git.personal.ksv741.ru/WealthLedger/docs/commit/82159e3b073ff0b4b5a0ceb5033a623b9d39f2e2))
|
|
||||||
* record Mantine breadcrumbs evidence ([5fd5e79](https://git.personal.ksv741.ru/WealthLedger/docs/commit/5fd5e79590986ecc9787fd26b548ec8bd5b08fc3))
|
|
||||||
* record OpenSpec lifecycle remediation ([9bb76e3](https://git.personal.ksv741.ru/WealthLedger/docs/commit/9bb76e35f9496e9414b678d325f6995bf413f144))
|
|
||||||
* record OpenSpec resilience evidence ([79727de](https://git.personal.ksv741.ru/WealthLedger/docs/commit/79727de9ffe61f36fbaca866e2993bde6243eace))
|
|
||||||
* record P1-11 SQLite verification ([66721ee](https://git.personal.ksv741.ru/WealthLedger/docs/commit/66721eee6295f1c432aaeb8631cfd3b0234d11d6))
|
|
||||||
* record React component style evidence ([6812fde](https://git.personal.ksv741.ru/WealthLedger/docs/commit/6812fdeae36faf93e682d1ec089bd2e839ea6fdc))
|
|
||||||
* record SDD audit remediation ([72c3a8a](https://git.personal.ksv741.ru/WealthLedger/docs/commit/72c3a8a6683b95f7e734044fdabe568668097c33))
|
|
||||||
* record SDD remediation evidence ([5233726](https://git.personal.ksv741.ru/WealthLedger/docs/commit/5233726855b9f3a8f1b8e0f64991e17f9e8be299))
|
|
||||||
* record second OpenSpec schema review ([d80e2e3](https://git.personal.ksv741.ru/WealthLedger/docs/commit/d80e2e32b2ebadcd93a53105e1ac337fe2c1f4f6))
|
|
||||||
* record session workflow evidence ([9f8280c](https://git.personal.ksv741.ru/WealthLedger/docs/commit/9f8280c567e6e3e659c899980e916805cdf46968))
|
|
||||||
* record stock card polish evidence ([6006400](https://git.personal.ksv741.ru/WealthLedger/docs/commit/60064003ede0afb99c1fa299b813c0be5f889ad7))
|
|
||||||
* record T-Bank cache boundary evidence ([598dfb2](https://git.personal.ksv741.ru/WealthLedger/docs/commit/598dfb2bdb2c0d5da41495f01edbea83f6e5c0d3))
|
|
||||||
* record verification simplification evidence ([1f030af](https://git.personal.ksv741.ru/WealthLedger/docs/commit/1f030af2df32cb392e272ce90f6fb0138a9d13b4))
|
|
||||||
* refresh project onboarding entrypoints ([88c0cd3](https://git.personal.ksv741.ru/WealthLedger/docs/commit/88c0cd3281f1b409779f0ec9365692291c651ffa))
|
|
||||||
* remove misplaced baseline design ([34577dc](https://git.personal.ksv741.ru/WealthLedger/docs/commit/34577dc7956f90f73c50ba067ff8ecc7e751853c))
|
|
||||||
* remove useless ([adc369b](https://git.personal.ksv741.ru/WealthLedger/docs/commit/adc369be79d1b4b4aad36168a8d32deab6568932))
|
|
||||||
* reorganize system knowledge by domain ([f20c3f4](https://git.personal.ksv741.ru/WealthLedger/docs/commit/f20c3f47bcacc4e65444c602c261c80691cab66f))
|
|
||||||
* simplify OpenSpec agent entrypoints ([eb0a96e](https://git.personal.ksv741.ru/WealthLedger/docs/commit/eb0a96e27c072e53f5c01db5a810502c104e8e6c))
|
|
||||||
* simplify system documentation ([9b19a5c](https://git.personal.ksv741.ru/WealthLedger/docs/commit/9b19a5cf4df5116c5f603f7189751d37ae9f0ffd))
|
|
||||||
* simplify verification workflow ([4af0a9c](https://git.personal.ksv741.ru/WealthLedger/docs/commit/4af0a9c05590d7d47135fc708719bf83d5dd3fb9))
|
|
||||||
* trust supplied OpenAPI snapshot ([2ef3759](https://git.personal.ksv741.ru/WealthLedger/docs/commit/2ef375919b84d9367791e9b22d761e506a5da930))
|
|
||||||
* update income analytics period constraints ([28a01a4](https://git.personal.ksv741.ru/WealthLedger/docs/commit/28a01a449acd907a93b6f45c86cd40543237395b))
|
|
||||||
* update portfolio view feature ([c628535](https://git.personal.ksv741.ru/WealthLedger/docs/commit/c6285352b11907b3542ba8fc502608828a834fa3))
|
|
||||||
|
|
||||||
### Refactoring
|
|
||||||
|
|
||||||
* little structure refactor ([1383949](https://git.personal.ksv741.ru/WealthLedger/docs/commit/1383949dc4c295d1b4e89abc372b780442ad91b7))
|
|
||||||
* refactor foundation ([5d5497a](https://git.personal.ksv741.ru/WealthLedger/docs/commit/5d5497a4e4fc52cd03326080ba082c6dec369ce5))
|
|
||||||
* refactor future event list ([680fc03](https://git.personal.ksv741.ru/WealthLedger/docs/commit/680fc03bec7ff5fdbac7c81c6daf8e5dc119abb5))
|
|
||||||
|
|
||||||
### CI
|
|
||||||
|
|
||||||
* verify docs and secure dependency updates [#1](https://git.personal.ksv741.ru/WealthLedger/docs/issues/1) [#5](https://git.personal.ksv741.ru/WealthLedger/docs/issues/5) [#6](https://git.personal.ksv741.ru/WealthLedger/docs/issues/6) ([c0a8cdb](https://git.personal.ksv741.ru/WealthLedger/docs/commit/c0a8cdb683953e515757071c288bfdf9e7b4b40a))
|
|
||||||
|
|
||||||
### Maintenance
|
|
||||||
|
|
||||||
* configure releases and develop workflow ([261f1ca](https://git.personal.ksv741.ru/WealthLedger/docs/commit/261f1ca6d15a894de470b94f2f0ad6fce3ae7917))
|
|
||||||
* **deps:** update node.js to v24.21.0 ([67f152d](https://git.personal.ksv741.ru/WealthLedger/docs/commit/67f152d3654b15c8d51b5bde95ac4e9e0e6fb253))
|
|
||||||
* document and adopt Node 24 LTS ([7bc8792](https://git.personal.ksv741.ru/WealthLedger/docs/commit/7bc8792b9853768734066c31a323b70886d73a23))
|
|
||||||
* save pending changes ([14dd879](https://git.personal.ksv741.ru/WealthLedger/docs/commit/14dd8790996b3461da8b1e3ec9a49d9526100047))
|
|
||||||
+10
-34
@@ -8,22 +8,18 @@ Docs даёт самостоятельное понимание продукта
|
|||||||
|
|
||||||
| Вид знания | Где поддерживается |
|
| Вид знания | Где поддерживается |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Согласованное проверяемое поведение: требования и сценарии | [Основные спецификации](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/AI_INDEX.md) в `specs/openspec/specs/` |
|
| Согласованное проверяемое поведение: требования и сценарии | [Основные спецификации](../specs/AI_INDEX.md) в `specs/openspec/specs/` |
|
||||||
| Предложения изменений, их проектирование и задачи | OpenSpec changes в существующем Store `wealth-ledger-specs`, `specs/openspec/changes/` |
|
| Предложения изменений, их проектирование и задачи | OpenSpec changes в существующем Store `wealth-ledger-specs`, `specs/openspec/changes/` |
|
||||||
| Понимание системы, областей и взаимодействий | Тематические страницы docs |
|
| Понимание системы, областей и взаимодействий | Тематические страницы docs |
|
||||||
| Принятые решения и подтверждённые причины | Единственный журнал [docs/adr](adr/README.md) |
|
| Принятые решения и подтверждённые причины | Единственный журнал [docs/adr](adr/README.md) |
|
||||||
| Конкретная реализация и технические контракты | Код, тесты, схемы, конфигурация; публичный OpenAPI формирует backend |
|
| Конкретная реализация и технические контракты | Код, тесты, схемы, конфигурация; публичный OpenAPI формирует backend |
|
||||||
| Размещение кода, conventions, команды разработки и проверки | Локальные инструкции frontend/backend |
|
| Размещение кода, conventions, команды разработки и проверки | Локальные инструкции frontend/backend |
|
||||||
| Работа над docs | Локальные [AGENTS](AGENTS.md) и этот документ |
|
| Выбор репозитория и типа знания | Корневой [AGENTS](../AGENTS.md) |
|
||||||
|
|
||||||
История аудитов и проверок остаётся в Git, Issues и PR. `CHANGELOG.md`
|
|
||||||
генерируется release-it из commits; вручную дублировать его на тематических страницах не нужно.
|
|
||||||
Процесс разработки и инструкции агентов принадлежат локальным инструкциям
|
|
||||||
репозиториев. ADR фиксируют решения самой системы, а не организацию работы.
|
|
||||||
|
|
||||||
Основные спецификации описывают и согласованное текущее поведение.
|
Основные спецификации описывают и согласованное текущее поведение.
|
||||||
Различие specs и docs не является разделением на будущее и настоящее.
|
Различие specs и docs не является разделением на будущее и настоящее.
|
||||||
Docs объясняет смысл контракта, не вводя независимо редактируемые требования.
|
Docs объясняет смысл контракта, не вводя независимо редактируемые требования.
|
||||||
|
OpenSpec применяется по явному запросу согласно [принятому процессу](development/openspec-authority-and-lifecycle.md).
|
||||||
|
|
||||||
## Потребность читателя и структура
|
## Потребность читателя и структура
|
||||||
|
|
||||||
@@ -86,11 +82,9 @@ Docs объясняет смысл контракта, не вводя неза
|
|||||||
Нужные точные значения сопровождай определяющим источником, чтобы не создать
|
Нужные точные значения сопровождай определяющим источником, чтобы не создать
|
||||||
конкурирующую норму. Паспорта страниц, Evidence-секции и номера строк не требуются.
|
конкурирующую норму. Паспорта страниц, Evidence-секции и номера строк не требуются.
|
||||||
|
|
||||||
Внутри docs используем относительные Markdown-ссылки. Внешние репозитории —
|
Внутри docs используем относительные Markdown-ссылки. Между репозиториями —
|
||||||
необязательные справочные веб-ссылки на опубликованный источник. Основной текст
|
относительный путь через соседний корень, например `../specs/AI_INDEX.md` из README.
|
||||||
и инструкции должны быть понятны без открытия этих ссылок. Проверяй локальные
|
Проверяй регистр, файл и якорь в фактическом workspace. Перенос требует исправить
|
||||||
файлы/якоря; для удалённых источников не обещай проверку их доступности CI.
|
|
||||||
Перенос требует исправить
|
|
||||||
входящие ссылки активных Markdown-документов; исторические архивы и dated audits
|
входящие ссылки активных Markdown-документов; исторические архивы и dated audits
|
||||||
сохраняют адреса своего времени. Короткий указатель оставляем только при реальной
|
сохраняют адреса своего времени. Короткий указатель оставляем только при реальной
|
||||||
необходимости совместимости, без второй копии содержания.
|
необходимости совместимости, без второй копии содержания.
|
||||||
@@ -155,8 +149,11 @@ npm run verify
|
|||||||
Существующий gate запускает Markdown lint с проверкой относительных ссылок
|
Существующий gate запускает Markdown lint с проверкой относительных ссылок
|
||||||
и якорей, затем `git diff --check`. Он охватывает новые Markdown-файлы;
|
и якорей, затем `git diff --check`. Он охватывает новые Markdown-файлы;
|
||||||
обычный diff их не показывает, поэтому отдельно просмотри untracked-содержание.
|
обычный diff их не показывает, поэтому отдельно просмотри untracked-содержание.
|
||||||
Markdown-only правки не требуют тестов приложения или соседнего checkout.
|
При изменениях соседних репозиториев выполни там `git diff --check` и проверь
|
||||||
|
затронутые ссылки. Markdown-only правки не требуют тестов всего приложения.
|
||||||
|
|
||||||
|
Точный scope исключений задан существующими `package.json` и конфигурацией lint:
|
||||||
|
исторические свидетельства не обязаны разрешать прежние адреса.
|
||||||
Правило relative-links не проверяет внешние URL и HTML-ссылки. Mermaid-рендер
|
Правило relative-links не проверяет внешние URL и HTML-ссылки. Mermaid-рендер
|
||||||
в gate не входит; используй доступный локальный рендерер без отправки материалов
|
в gate не входит; используй доступный локальный рендерер без отправки материалов
|
||||||
наружу, установки стека или добавления новых валидаторов. Публикационного
|
наружу, установки стека или добавления новых валидаторов. Публикационного
|
||||||
@@ -166,24 +163,3 @@ Markdown-only правки не требуют тестов приложения
|
|||||||
разработчик от вопроса к объяснению и требованию; агент от типа задачи к нужным
|
разработчик от вопроса к объяснению и требованию; агент от типа задачи к нужным
|
||||||
ограничениям, ADR и репозиторию. Отчёт различает синтаксис, ссылки, рендер,
|
ограничениям, ADR и репозиторию. Отчёт различает синтаксис, ссылки, рендер,
|
||||||
смысловую сверку и оставшиеся ограничения.
|
смысловую сверку и оставшиеся ограничения.
|
||||||
|
|
||||||
### CI и обновления зависимостей
|
|
||||||
|
|
||||||
`.gitea/workflows/verify.yml` запускает этот gate на push и PR с Node
|
|
||||||
из `.nvmrc` и установкой через `npm ci`. Скачивается только docs;
|
|
||||||
его SHA сохраняется в логе. Ошибка checkout, Markdown или внутренней ссылки
|
|
||||||
завершает job с FAIL. Внешние веб-ссылки — справочные; CI не проверяет их
|
|
||||||
доступность, Mermaid-render или semantic correctness.
|
|
||||||
|
|
||||||
`security.yml` при push, PR и ежедневно проверяет всю Git-историю через
|
|
||||||
Gitleaks 8.30.1 с redacted-выводом и все npm dependencies через
|
|
||||||
`npm audit --include=dev --audit-level=low`. Overrides `smol-toml` и `js-yaml`
|
|
||||||
исправляют DoS в parsers markdownlint-cli; overrides для release-it обновляют
|
|
||||||
уязвимые basic-ftp и undici. Их можно удалить, когда родительские пакеты
|
|
||||||
примут исправленные версии.
|
|
||||||
|
|
||||||
Renovate объединяет minor/patch updates; major требует согласования в
|
|
||||||
Dependency Dashboard, automerge не включён. Required checks включаются
|
|
||||||
отдельно в Gitea; workflow сам по себе не запрещает merge.
|
|
||||||
|
|
||||||
Версии и ветки: [процесс релиза](README.md#релизы).
|
|
||||||
|
|||||||
@@ -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) | Поиск выпуска, рыночные показатели и личная история |
|
||||||
|
|
||||||
## Найти материал по задаче
|
## Найти материал по задаче
|
||||||
|
|
||||||
@@ -44,88 +44,32 @@ WealthLedger помогает частному инвестору понимат
|
|||||||
| Разобраться в системе | [Архитектура](architecture/README.md), нужная область выше, [внешние интеграции](integrations/README.md) |
|
| Разобраться в системе | [Архитектура](architecture/README.md), нужная область выше, [внешние интеграции](integrations/README.md) |
|
||||||
| Найти точные сведения | [Глоссарий](glossary.md), [показатели портфеля](domains/portfolio/data-reference.md), [HTTP API](architecture/http-api.md) |
|
| Найти точные сведения | [Глоссарий](glossary.md), [показатели портфеля](domains/portfolio/data-reference.md), [HTTP API](architecture/http-api.md) |
|
||||||
| Разобрать расхождение в суммах | [Диагностика портфеля](domains/portfolio/how-to/investigate-discrepancy.md) |
|
| Разобрать расхождение в суммах | [Диагностика портфеля](domains/portfolio/how-to/investigate-discrepancy.md) |
|
||||||
|
| Проверить условия локальной работы | [Окружение и восстановление](development/local-workspace.md) |
|
||||||
| Понять ошибку или старые данные | [Состояния интерфейса](architecture/cross-cutting/ui-resilience.md), [свежесть данных](architecture/cross-cutting/data-freshness.md) |
|
| Понять ошибку или старые данные | [Состояния интерфейса](architecture/cross-cutting/ui-resilience.md), [свежесть данных](architecture/cross-cutting/data-freshness.md) |
|
||||||
| Понять визуальные роли и состояния | [Визуальные принципы](design/overview.md) |
|
| Найти визуальные роли и примеры | [Дизайн-система](development/design-system.md), [UI-каталог](development/ui-catalog.md) |
|
||||||
| Узнать причину архитектурного решения | [Журнал ADR](adr/README.md) |
|
| Узнать причину архитектурного решения | [Журнал ADR](adr/README.md) |
|
||||||
| Запустить, обновить или восстановить приложение | [Контейнерная поставка](architecture/deployment.md) |
|
| Изменить или проверить систему | [Сопровождение](development/README.md), затем инструкции нужного репозитория |
|
||||||
|
|
||||||
|
Практический tutorial первого инвестиционного сценария пока не подготовлен:
|
||||||
|
он требует подключения брокера. Маршрут первого знакомства выше — чтение, не учебный запуск.
|
||||||
|
UI-каталог содержит безопасные вымышленные примеры и поясняет границы их использования.
|
||||||
|
|
||||||
## Границы знаний
|
## Границы знаний
|
||||||
|
|
||||||
[Основные спецификации](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/AI_INDEX.md) сохраняют согласованное проверяемое
|
[Основные спецификации](../specs/AI_INDEX.md) сохраняют согласованное проверяемое
|
||||||
поведение, а OpenSpec changes — предложения, дизайн и задачи изменений.
|
поведение, а OpenSpec changes — предложения, дизайн и задачи изменений.
|
||||||
Фактический Store находится в `specs/openspec/`, основные требования — в
|
Фактический Store находится в `specs/openspec/`, основные требования — в
|
||||||
`specs/openspec/specs/`. Docs объясняет систему; код, тесты, схемы и конфигурация
|
`specs/openspec/specs/`. Docs объясняет систему; код, тесты, схемы и конфигурация
|
||||||
задают конкретный механизм, а [ADR](adr/README.md) сохраняют принятые решения.
|
задают конкретный механизм, а [ADR](adr/README.md) сохраняют принятые решения.
|
||||||
Локальные инструкции [frontend](https://git.personal.ksv741.ru/WealthLedger/frontend/src/commit/9f7e342333b80bfacfd5bc394a2dcd0613d33ff3/AGENTS.md) и [backend](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/AGENTS.md)
|
Локальные инструкции [frontend](../frontend/AGENTS.md) и [backend](../backend/AGENTS.md)
|
||||||
описывают изменение реализации и её проверки.
|
описывают изменение реализации и её проверки.
|
||||||
|
|
||||||
Эти источники дополняют друг друга. Найденный незавершённый change не доказывает
|
Эти источники дополняют друг друга. Найденный незавершённый change не доказывает
|
||||||
реализованную функцию; расхождение кода и требования требует разбора.
|
реализованную функцию; расхождение кода и требования требует разбора.
|
||||||
Итоги исследований входят в ADR, когда принято значимое решение, и в тематические
|
Итоги исследований входят в ADR, когда принято значимое решение, и в тематические
|
||||||
страницы, когда уточняют знания о системе. Исходные исследовательские отчёты
|
страницы, когда уточняют знания о системе. Исходные исследовательские отчёты
|
||||||
в docs не хранятся. История изменений остаётся в Git; документация описывает
|
в docs не хранятся. [Исторические свидетельства](development/README.md#исторические-свидетельства)
|
||||||
текущее устройство системы и причины архитектурных решений.
|
сохраняют результаты своего времени и не заменяют текущее описание системы.
|
||||||
|
|
||||||
Правила изменения текста, диаграмм, ссылок и проверки docs-impact находятся
|
Правила изменения текста, диаграмм, ссылок и проверки docs-impact находятся
|
||||||
в [CONTRIBUTING](CONTRIBUTING.md).
|
в [CONTRIBUTING](CONTRIBUTING.md).
|
||||||
|
|
||||||
## Релизы
|
|
||||||
|
|
||||||
Репозиторий выпускается независимо. `develop` — интеграция, `main` — принятые
|
|
||||||
релизы. Обычная работа: issue-linked ветка от актуального `develop`, PR в `develop`;
|
|
||||||
общие правила — в Engineering Workflow по ссылке из AGENTS.
|
|
||||||
PR сливается squash или rebase-merge; merge-коммит не используется. `main`
|
|
||||||
обновляется только fast-forward из `develop`, поэтому история линейна:
|
|
||||||
|
|
||||||
```text
|
|
||||||
<type>/<issue>-<slug> --squash--> develop --ff-only--> main --> tag vX.Y.Z
|
|
||||||
```
|
|
||||||
|
|
||||||
Инвариант: `origin/main` всегда предок `origin/develop`. Проверка:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git fetch -q origin
|
|
||||||
git merge-base --is-ancestor origin/main origin/develop && echo ok || echo BROKEN
|
|
||||||
```
|
|
||||||
|
|
||||||
Commits и PR titles — Conventional Commits со ссылкой на Issue (кроме release),
|
|
||||||
включая `spec:`. Версии вручную не повышай.
|
|
||||||
|
|
||||||
1. Получи `main`, `develop` и теги: `git fetch origin --tags`. Проверь
|
|
||||||
инвариант. Создай `release/X.Y.Z` от проверенного `develop`;
|
|
||||||
`nvm use --silent`, `npm ci`.
|
|
||||||
2. На чистой ветке выполни `npm run release:preview`, просмотри версию/changelog,
|
|
||||||
затем `npm run release:prepare`. Команда создаёт `chore(release): X.Y.Z`,
|
|
||||||
обновляет manifest/lockfile/CHANGELOG; тег и push не выполняет. Не обходи
|
|
||||||
guards ветки, чистого дерева, отсутствующих тегов или уже подготовленной версии.
|
|
||||||
3. Проверь diff и штатные gates этого репозитория. Отправь только release-ветку,
|
|
||||||
открой PR в **`develop`** с заголовком `chore(release): X.Y.Z`. После зелёных
|
|
||||||
`verify`, `security`, `commits` выполни fast-forward-only merge. Если
|
|
||||||
`develop` изменился, обнови release-ветку и повтори проверки кандидата.
|
|
||||||
4. Открой PR `develop → main` с тем же заголовком и слей fast-forward-only.
|
|
||||||
5. Получи удалённый `main`, сравни его HEAD с проверенным release SHA и версией
|
|
||||||
manifest/lockfile. Создай аннотированный `vX.Y.Z` именно на этом SHA:
|
|
||||||
`git tag -a vX.Y.Z <release-sha> -m "Release X.Y.Z"`, затем отправь только
|
|
||||||
`git push origin refs/tags/vX.Y.Z`. Не запускай release-it повторно на `main`.
|
|
||||||
|
|
||||||
Обратная синхронизация не нужна: release-коммит попал в `develop` на шаге 3.
|
|
||||||
Опубликованные теги не перемещай. При сбое после merge создай отсутствующий тег
|
|
||||||
без нового bump; до merge исправь и проверь существующий кандидат без
|
|
||||||
повторного повышения версии.
|
|
||||||
|
|
||||||
Freeze релиза не поддерживается: пока открыт релизный PR, `develop` не двигают.
|
|
||||||
Срочный fix — обычная ветка `fix/*` от `develop` с последующим релизом;
|
|
||||||
пути от `main` нет, он сделал бы `main` не-предком `develop`. Исключение
|
|
||||||
требует явного решения: `release/hotfix-<name>` от `main`, PR в `main`, тег,
|
|
||||||
затем merge `main → develop` для восстановления инварианта.
|
|
||||||
|
|
||||||
`feat:` повышает minor, `fix:`/`perf:` и прочие разрешённые типы — patch;
|
|
||||||
`!`/`BREAKING CHANGE:` — major, включая версии до 1.0. `chore(release):`
|
|
||||||
исключён из рекомендации и changelog. Конфигурация — `.release-it.json`.
|
|
||||||
В Gitea для `main`/`develop` должны быть запрещены прямой/force push и обход
|
|
||||||
администратором, включена блокировка устаревшей ветки, обязательны `verify`,
|
|
||||||
`security`, `commits`; теги `v*` защищены. Линейность обеспечивают merge styles
|
|
||||||
репозитория: merge-коммиты и rebase `--no-ff` выключены, доступны squash,
|
|
||||||
rebase-merge и fast-forward-only; стиль по умолчанию — squash.
|
|
||||||
Workflow сам не включает эти настройки. Отдельное review approval для одного
|
|
||||||
владельца не требуется. Npm publishing, deploy и Gitea Releases не включены.
|
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
**Status:** Accepted, baseline decision
|
**Status:** Accepted, baseline decision
|
||||||
|
|
||||||
Deployment boundary расширена [ADR 015](015-single-server-sqlite-deployment.md)
|
|
||||||
для одного сервера и одного backend process. Ниже сохранён исходный контекст.
|
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
MVP нужен воспроизводимый локальный доступ к данным без инфраструктуры развёртывания. Доменные таблицы и миграции на baseline отсутствуют.
|
MVP нужен воспроизводимый локальный доступ к данным без инфраструктуры развёртывания. Доменные таблицы и миграции на baseline отсутствуют.
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
**Status:** Accepted; supersedes the refresh-token reuse rule in ADR-006.
|
**Status:** Accepted; supersedes the refresh-token reuse rule in ADR-006.
|
||||||
|
|
||||||
Cookie-clearing policy уточнена в [ADR 018](018-minimal-runtime-coordination.md):
|
|
||||||
неуспешный refresh больше не удаляет cookie. CAS и отказ superseded token сохранены.
|
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
Два одновременных запроса могут предъявить один действительный refresh token. Отзыв Session для проигравшего запроса делает недействительной сессию успешного запроса, а сохранение отдельной истории token или grace-window для различения replay добавляет непропорциональное состояние локальному MVP.
|
Два одновременных запроса могут предъявить один действительный refresh token. Отзыв Session для проигравшего запроса делает недействительной сессию успешного запроса, а сохранение отдельной истории token или grace-window для различения replay добавляет непропорциональное состояние локальному MVP.
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
status: accepted
|
||||||
|
---
|
||||||
|
|
||||||
|
# Native OpenSpec SDD и минимальная local orchestration
|
||||||
|
|
||||||
|
## Статус: историческое решение
|
||||||
|
|
||||||
|
[ADR 012](012-task-driven-development-with-scoped-context.md) отменяет обязательный
|
||||||
|
SDD lifecycle. Native OpenSpec остаётся optional инструментом; repo gates сохраняются.
|
||||||
|
|
||||||
|
### Прежняя частичная замена
|
||||||
|
|
||||||
|
[ADR 011](011-native-openspec-lifecycle-with-repository-gates.md) заменяет части
|
||||||
|
этого решения о schema v3, custom verifiers и dispatcher. Выбор native OpenSpec,
|
||||||
|
независимых repository gates и исходное rationale сохраняются. Ниже оставлено
|
||||||
|
историческое описание прежней реализации; текущие принципы работы описаны
|
||||||
|
в ADR 012. Version contract этим уточнением не меняется.
|
||||||
|
|
||||||
|
## Исторический контекст и решение
|
||||||
|
|
||||||
|
На момент решения WealthLedger использовал проектную schema `wealth-ledger-sdd` version 3 с
|
||||||
|
последовательностью `Proposal → Delta Specs → Design → Tasks → Apply`, один exact
|
||||||
|
OpenSpec CLI contract и проверки без изменения проверяемых материалов. Это
|
||||||
|
сохраняло единый источник правил жизненного цикла в хранилище спецификаций, выражало approval
|
||||||
|
boundaries нативными extension points и не вводит второй workflow engine или
|
||||||
|
monorepo control plane для четырёх независимых Git-репозиториев.
|
||||||
|
|
||||||
|
## Рассмотренные варианты
|
||||||
|
|
||||||
|
- Встроенная `spec-driven` schema отклонена: она не выражает обязательный переход Specs перед Design и не задаёт Delivery Map или проектные роли артефактов.
|
||||||
|
- Отдельный workflow engine или custom generator отклонён: он создал бы конкурирующий формат и lifecycle authority.
|
||||||
|
- Nx, Turborepo, Bazel, Make или just отклонены для текущего масштаба: workspace не имеет общего dependency graph, build cache или CI, а репозитории уже владеют canonical gates.
|
||||||
|
- Versioned source tree, patch-only, programmatic generator и symlink для root
|
||||||
|
agent entrypoints отклонены для текущего масштаба: они добавляют отдельный
|
||||||
|
projection lifecycle поверх уже достаточных schema, docs и scripts.
|
||||||
|
|
||||||
|
## Исторические последствия
|
||||||
|
|
||||||
|
OpenSpec CLI закреплён exact version contract; новая stable release не включает
|
||||||
|
auto-upgrade, а смена contract является отдельным SDD change со staged
|
||||||
|
проверками совместимости. Локальная точка входа оставалась навигацией,
|
||||||
|
а обязательные ограничения проверялись отдельно. Planning
|
||||||
|
verifier до approval проверяет только детерминированную структуру: capability
|
||||||
|
set, Delivery Map, rollback, task area и Requirement coverage. Он различает
|
||||||
|
artifact-validation, неверный invocation и failure окружения, чтобы agent не
|
||||||
|
переписывал planning artifact из-за сломанного вызова или prerequisite.
|
||||||
|
|
||||||
|
Dispatcher знает четыре workspace area, но не реализует build или test logic:
|
||||||
|
он делегирует repo-owned commands. Approval identity остаётся человеческим
|
||||||
|
workflow constraint, потому что schema моделирует порядок артефактов, но не
|
||||||
|
личность утвердившего. Semantic review по-прежнему не заменяется structural
|
||||||
|
checks.
|
||||||
|
|
||||||
|
Решение пересматривается, если OpenSpec перестанет поддерживать нужный artifact graph, появится обязательный общий CI/build graph или стоимость последовательной локальной orchestration станет существенной. В этих случаях новый ADR сравнивает native evolution с внешним engine либо monorepo platform.
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
status: superseded
|
||||||
|
---
|
||||||
|
|
||||||
|
# Native OpenSpec lifecycle и repository gates
|
||||||
|
|
||||||
|
Обязательность описанной ниже процедуры заменена
|
||||||
|
[ADR 012](012-task-driven-development-with-scoped-context.md). Текст сохраняет
|
||||||
|
историческое решение; current policy — прямая работа по задаче.
|
||||||
|
|
||||||
|
## Контекст
|
||||||
|
|
||||||
|
[ADR 010](010-native-openspec-sdd-and-local-orchestration.md) сохраняет rationale
|
||||||
|
выбора native OpenSpec и независимых repository gates. Его описание schema v3,
|
||||||
|
custom authority/planning verifiers и dispatcher относится к прежней реализации.
|
||||||
|
На момент решения проектная schema `wealth-ledger-sdd`
|
||||||
|
имела version 5 и граф `Proposal → Delta Specs → Design → Tasks → Apply`.
|
||||||
|
Порядок работы определялся средствами OpenSpec и общими правилами проекта;
|
||||||
|
дополнительные точки входа служили навигацией по единой процедуре.
|
||||||
|
|
||||||
|
Версия OpenSpec CLI была зафиксирована как `1.10.0`.
|
||||||
|
Версия schema и CLI contract — разные сведения. Фиксация версии
|
||||||
|
не доказывает запуск прежних проверок; это решение не меняет CLI pin,
|
||||||
|
version contract, schema или требования к tooling.
|
||||||
|
|
||||||
|
## Решение
|
||||||
|
|
||||||
|
Частично заменить ADR 010 в части прежней schema version и orchestration.
|
||||||
|
Единым хранилищем согласования оставался `wealth-ledger-specs`.
|
||||||
|
Процедура разделяла следующие ответственности:
|
||||||
|
|
||||||
|
- Согласование ролей, подтверждений, объёма поставки и обязательных проверок.
|
||||||
|
- Планирование и применение изменений, структурную проверку и смысловое согласование требований.
|
||||||
|
- Выбор и повторную сверку исходных знаний, восстановление контекста и повторное использование свидетельств.
|
||||||
|
|
||||||
|
Native OpenSpec получает structure и contextFiles текущего artifact или Apply.
|
||||||
|
Каждое изменяемое приложение владело своей проверкой качества. Выполнение
|
||||||
|
процедуры не зависит от custom verifier, dispatcher, skill или session adapter;
|
||||||
|
optional entrypoints остаются navigation, без второго workflow engine.
|
||||||
|
|
||||||
|
## Последствия
|
||||||
|
|
||||||
|
Люди и автоматизированные инструменты следовали единой процедуре, а каждый
|
||||||
|
репозиторий отвечал за собственные проверки. ADR фиксировал причину этого
|
||||||
|
разделения ответственности. Исходные альтернативы и rationale остаются
|
||||||
|
в ADR 010; historical archives, audits и evidence сохраняют описание своего времени.
|
||||||
|
|
||||||
|
Structural success не подтверждает решение Change Owner, semantic compliance
|
||||||
|
или независимый PASS. Approval bindings, полный применимый context/Design
|
||||||
|
package, независимая Validation и отдельная Finalization сохраняются.
|
||||||
|
Замена orchestration не ослабляет gates и не разрешает изменение незатронутых
|
||||||
|
main-spec requirements о tooling.
|
||||||
|
|
||||||
|
Текущие принципы сопровождения описаны в
|
||||||
|
[engineering governance](../development/engineering-governance.md), роль
|
||||||
|
сохранённых требований — в [OpenSpec](../development/openspec-authority-and-lifecycle.md).
|
||||||
|
Эти материалы отражают последующее упрощение процесса, а не восстанавливают
|
||||||
|
обязательность исторической процедуры.
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# 012. Прямая разработка с контекстом по задаче
|
||||||
|
|
||||||
|
**Status:** Accepted; supersedes обязательный lifecycle из ADR 010/011.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Обязательная цепочка OpenSpec artifacts не обеспечила соответствие исходному
|
||||||
|
намерению: суженный change мог пройти все проверки, оставив backend и основные
|
||||||
|
рекомендации исследований вне реализации. Владелец проекта выбрал прямую
|
||||||
|
разработку без обязательного OpenSpec/SDD.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Задача пользователя определяет результат. Рабочий контекст выбирается по
|
||||||
|
предмету изменения: локальные правила разработки отделены от проектных фактов,
|
||||||
|
а тематические источники объясняют соответствующую область. Общие принципы
|
||||||
|
и согласование контракта не дублируются в независимых описаниях.
|
||||||
|
|
||||||
|
[Engineering governance](../development/engineering-governance.md) и
|
||||||
|
[context discipline](../development/agent-token-efficiency.md) описывают текущую
|
||||||
|
работу. Repo gates, trust-boundary checks, migration safety и semantic review
|
||||||
|
сохраняются. OpenSpec используется только по явному запросу; history сохраняется.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
Обычная задача не требует planning artifacts, role handoffs, approval fingerprints
|
||||||
|
и archive. Сложные public/security/data решения уточняются по существу до опасной
|
||||||
|
операции. Правильность routing проверяется на задачах и counterexamples; качество
|
||||||
|
агента оценивается по наблюдаемым outcomes, а не по числу файлов или checkboxes.
|
||||||
|
|
||||||
|
Рекомендации исследований адаптируются к принятой архитектуре и существующим
|
||||||
|
проверкам WealthLedger. Backend остаётся источником OpenAPI. Новые сервисы,
|
||||||
|
общая инфраструктура и проверки не вводятся только ради организации контекста.
|
||||||
@@ -15,6 +15,11 @@
|
|||||||
Это давало частичное покрытие одного агентства без покупки доступа,
|
Это давало частичное покрытие одного агентства без покупки доступа,
|
||||||
но не рейтинг любой российской облигации и не стабильный контракт API.
|
но не рейтинг любой российской облигации и не стабильный контракт API.
|
||||||
|
|
||||||
|
Основания выбора сохранены из исследования, а применение решения подтверждено
|
||||||
|
backend-коммитом `235c179` (`feat: add bond details, yields and credit ratings`).
|
||||||
|
Исходное исследование содержало также рекомендации; эта запись фиксирует
|
||||||
|
выбранный подход, не объявляя все рекомендации реализованными.
|
||||||
|
|
||||||
## Решение
|
## Решение
|
||||||
|
|
||||||
Использовать публичный [реестр выпусков НКР](https://ratings.ru/ratings/issues/)
|
Использовать публичный [реестр выпусков НКР](https://ratings.ru/ratings/issues/)
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
# 014. Актуальная OpenAPI-схема как вход frontend
|
|
||||||
|
|
||||||
**Статус:** принято 2026-09-13.
|
|
||||||
|
|
||||||
## Контекст
|
|
||||||
|
|
||||||
Frontend и backend — независимые репозитории. Зависимость frontend от путей
|
|
||||||
и команд backend связывала бы их внутреннее устройство. Предоставленная
|
|
||||||
OpenAPI-схема служит границей между владельцем API и его потребителем.
|
|
||||||
|
|
||||||
## Решение
|
|
||||||
|
|
||||||
Backend остаётся источником HTTP-контракта согласно
|
|
||||||
[ADR 002](002-nest-generated-openapi-contract.md).
|
|
||||||
Frontend принимает предоставленную OpenAPI-схему как актуальную и генерирует
|
|
||||||
из неё типы локально, без доступа к соседнему backend checkout.
|
|
||||||
Подготовка и доставка схемы находятся вне frontend.
|
|
||||||
|
|
||||||
## Последствия
|
|
||||||
|
|
||||||
Frontend зависит от публичного контракта, а не от внутреннего устройства
|
|
||||||
backend. Соответствие типов входной схеме не гарантирует её актуальность:
|
|
||||||
рассинхронизацию предоставленной схемы и работающего API frontend не выявляет
|
|
||||||
сравнением схем. Актуальность входа остаётся принятым допущением.
|
|
||||||
OpenAPI endpoint не является runtime-зависимостью SPA.
|
|
||||||
|
|
||||||
Текущее взаимодействие описано в [HTTP API](../architecture/http-api.md).
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
# 015. SQLite в ограниченном single-server deployment
|
|
||||||
|
|
||||||
**Status:** Accepted
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
[ADR 001](001-prisma-sqlite-local-mvp.md) ограничивал SQLite локальным MVP.
|
|
||||||
Контейнерная поставка требует постоянного хранилища на одном сервере,
|
|
||||||
но не требует высокой доступности или нескольких экземпляров API.
|
|
||||||
Пользователи, сессии, зашифрованные подключения и банковская история уже
|
|
||||||
хранятся в SQLite. Кэши, limiter и generation подключений локальны процессу.
|
|
||||||
|
|
||||||
## Decision
|
|
||||||
|
|
||||||
Разрешён один backend process на одном Docker host с SQLite в постоянном
|
|
||||||
host directory. Development использует отдельный named volume. SQLite
|
|
||||||
остаётся embedded library без сетевого порта. Это расширяет deployment
|
|
||||||
boundary ADR 001; его исторический контекст сохраняется.
|
|
||||||
|
|
||||||
Prisma migrations выполняются отдельным одноразовым service из того же image,
|
|
||||||
что и backend. Перед изменением схемы backend останавливается, всё data
|
|
||||||
directory копируется вместе с возможными WAL/SHM files. Ключ шифрования
|
|
||||||
сохраняется отдельно. Неуспешная migration блокирует запуск новой версии.
|
|
||||||
|
|
||||||
Откат image требует совместимости с текущей схемой и не восстанавливает
|
|
||||||
старую базу. Restore требует отдельного решения о потере данных после backup.
|
|
||||||
Frontend и backend имеют независимые версии; production фиксирует version tags.
|
|
||||||
Краткий простой допустим.
|
|
||||||
|
|
||||||
## Alternatives
|
|
||||||
|
|
||||||
PostgreSQL потребовал бы миграции данных и отдельного database lifecycle;
|
|
||||||
текущие требования этого не оправдывают. Kubernetes, replicas и blue-green
|
|
||||||
не устраняют ограничения process-local состояния и не входят в решение.
|
|
||||||
|
|
||||||
## Consequences
|
|
||||||
|
|
||||||
Сервер не обещает HA или zero-downtime. Один backend instance — обязательное
|
|
||||||
ограничение, а не рекомендуемая настройка масштабирования. Нужны контроль
|
|
||||||
свободного места, согласованные backups и проверяемое восстановление.
|
|
||||||
|
|
||||||
Решение пересматривается при необходимости нескольких replicas, другом
|
|
||||||
availability target, заметном write contention или потребности в отдельном
|
|
||||||
database service. Добавление реплик до такого решения недопустимо.
|
|
||||||
|
|
||||||
Топология описана в [архитектуре](../architecture/containers.md#контейнерная-поставка),
|
|
||||||
операции — в [runbook](../architecture/deployment.md).
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
# 016. Версионируемый комплект развёртывания
|
|
||||||
|
|
||||||
**Status:** Accepted
|
|
||||||
|
|
||||||
Frontend и backend выпускаются независимо. Выбор их версий непосредственно
|
|
||||||
на сервере перекладывает подбор совместимой пары на оператора при каждом
|
|
||||||
обновлении. Принято решение выпускать комплект в репозитории `deploy`:
|
|
||||||
его релиз закрепляет проверенную пару конкретных версий обоих образов.
|
|
||||||
Независимые релизы компонентов сохраняются; выпуск компонента сам по себе
|
|
||||||
не означает готовность нового релиза развёртывания.
|
|
||||||
|
|
||||||
Оператор подключается к серверу по SSH и одной командой запускает обновление
|
|
||||||
до выбранного релиза развёртывания. При обновлении backend команда должна
|
|
||||||
скачать образы до его остановки, сохранить резервную копию остановленной базы,
|
|
||||||
выполнить миграции и проверить запуск. Ограничения
|
|
||||||
[ADR 015](015-single-server-sqlite-deployment.md)
|
|
||||||
сохраняются: один backend, краткий простой, неуспешная миграция блокирует запуск;
|
|
||||||
откат образа не восстанавливает базу автоматически.
|
|
||||||
|
|
||||||
## Доставка и подготовка сервера
|
|
||||||
|
|
||||||
На сервере хранится Git checkout `deploy`. Команда обновления получает
|
|
||||||
выбранный тег комплекта и загружает закреплённые образы из Nexus;
|
|
||||||
сборка приложения на сервере не требуется.
|
|
||||||
|
|
||||||
Docker с Compose, внешний HTTPS и доступ к registry считаются подготовленными.
|
|
||||||
README комплекта должен описывать последовательность этой разовой подготовки
|
|
||||||
и подключения существующей установки. Автоматическое provisioning сервера
|
|
||||||
в комплект не входит.
|
|
||||||
|
|
||||||
Рабочие данные, сертификат доверия T-Bank и секреты остаются вне обновляемого
|
|
||||||
checkout. При подключении существующей установки сохраняются её БД,
|
|
||||||
JWT secret и ключ шифрования. Сертификат `certs/tbank-ca.pem` монтируется
|
|
||||||
в контейнер только для чтения; путь внутри контейнера задаётся через
|
|
||||||
`TBANK_CA_CERT_PATH`. Этот сертификат не обслуживает входящий HTTPS.
|
|
||||||
|
|
||||||
## Резервные копии
|
|
||||||
|
|
||||||
Перед обновлением backend создаётся согласованная копия всего каталога данных
|
|
||||||
при остановленном backend. Копии хранятся в локальном `backups/` вне checkout,
|
|
||||||
без автоматического удаления; очистку выполняет оператор вручную.
|
|
||||||
Такие копии позволяют восстановиться после неудачного обновления, но сами
|
|
||||||
по себе не защищают от потери диска сервера.
|
|
||||||
|
|
||||||
## Обновление и сбои
|
|
||||||
|
|
||||||
Если релиз меняет только frontend, обновляется только его контейнер:
|
|
||||||
backend не останавливается, миграции и резервное копирование БД для этого
|
|
||||||
обновления не выполняются. Этот путь применим только при неизменных образе
|
|
||||||
и конфигурации backend.
|
|
||||||
|
|
||||||
Если миграции прошли, но новый backend не прошёл проверку запуска, обновление
|
|
||||||
завершается с ошибкой, неисправный backend останавливается. Оператор получает
|
|
||||||
диагностику, сведения о предыдущей версии и путь к резервной копии.
|
|
||||||
Автоматического отката нет: прежний образ может быть несовместим с новой
|
|
||||||
схемой. Откат образа и восстановление базы остаются отдельными явными действиями
|
|
||||||
по правилам ADR 015.
|
|
||||||
|
|
||||||
## Выпуск комплекта
|
|
||||||
|
|
||||||
После публикации образов компонентов отдельно готовится релиз `deploy`:
|
|
||||||
фиксируется совместимая пара версий, выполняется её проверка и выпускается
|
|
||||||
тег комплекта. Публикация компонента не создаёт релиз комплекта автоматически
|
|
||||||
и не запускает обновление сервера. Оператор выбирает выпущенный тег при
|
|
||||||
запуске команды развёртывания.
|
|
||||||
|
|
||||||
## Состояние реализации
|
|
||||||
|
|
||||||
Комплект и команда реализованы в отдельном репозитории `deploy`.
|
|
||||||
Публикация первого тега и обновление рабочего сервера — отдельные действия.
|
|
||||||
[Контейнерная поставка](../architecture/deployment.md) описывает границы,
|
|
||||||
пошаговая инструкция находится в README комплекта.
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
# 017. Локальное чтение данных T-Bank и фоновая синхронизация
|
|
||||||
|
|
||||||
**Status:** Accepted — историческое архитектурное направление. Решения ниже о
|
|
||||||
request-driven запуске, ручном обновлении и отсутствии периодического обхода
|
|
||||||
заменены [ADR 019](019-hourly-reads-and-online-instrument-reference.md).
|
|
||||||
|
|
||||||
Prefetch policy ниже заменена [ADR 018](018-minimal-runtime-coordination.md):
|
|
||||||
свечи загружаются по запрошенному диапазону, без предварительной загрузки
|
|
||||||
всей истории бумаг портфеля. Остальные гарантии локального хранения сохранены.
|
|
||||||
|
|
||||||
Реализация `persist-tbank-data` добавлена в рабочие ветки приложения;
|
|
||||||
performance/browser и canonical application gates пройдены. Это не отметка
|
|
||||||
развёртывания в production. Ниже сохранён контекст
|
|
||||||
принятия решения, а текущее устройство описывает
|
|
||||||
[модель свежести](../architecture/cross-cutting/data-freshness.md).
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
Инвестиционная история не сохраняется в БД. Серверные кэши находятся в памяти
|
|
||||||
процесса, а загрузка операций, аналитики дохода и рыночной истории зависит
|
|
||||||
от повторных обращений к T-Bank. Для журнала операций запрос доступных счетов
|
|
||||||
выполняется даже перед чтением кэшированной страницы. Сохранение только
|
|
||||||
ответов без изменения этого пути не устранит ожидание внешнего API.
|
|
||||||
|
|
||||||
Нужно ускорить все экраны, использующие T-Bank, и уменьшить повторную загрузку
|
|
||||||
данных. Текущие механизмы описаны в [политике свежести](../architecture/cross-cutting/data-freshness.md)
|
|
||||||
и [журнале операций](../domains/operations/overview.md).
|
|
||||||
|
|
||||||
## Decision
|
|
||||||
|
|
||||||
Для уже загруженных данных обычный запрос frontend обслуживается из постоянного
|
|
||||||
локального хранилища без ожидания ответа T-Bank. Обновление выполняется в фоне.
|
|
||||||
Пользователь видит время последнего успешного обновления и может запросить
|
|
||||||
обновление вручную. Сохранённый портфель может ещё не содержать новую сделку;
|
|
||||||
эта задержка принята ради быстрого открытия экрана.
|
|
||||||
|
|
||||||
Синхронизация запускается только запросами пользовательских экранов и ручным
|
|
||||||
действием «Обновить». Периодических запусков, ежедневного обновления неактивных
|
|
||||||
пользователей и плановой сверки истории нет. Фоновое выполнение означает,
|
|
||||||
что уже запрошенная работа не задерживает выдачу сохранённых данных;
|
|
||||||
самостоятельное расписание обновлений оно не подразумевает.
|
|
||||||
|
|
||||||
Обычный запрос запускает обновление, только если нужных данных нет либо
|
|
||||||
истёк согласованный срок их свежести:
|
|
||||||
|
|
||||||
| Данные | Срок свежести |
|
|
||||||
| --- | --- |
|
|
||||||
| Портфель и текущие цены | 1 минута |
|
|
||||||
| Операции текущего месяца | 5 минут |
|
|
||||||
| Список доступных счетов | 5 минут |
|
|
||||||
| Справочник инструментов и календари купонов и дивидендов | 1 сутки |
|
|
||||||
| Незавершённая дневная свеча | 1 минута |
|
|
||||||
|
|
||||||
Эти сроки проверяются при запросе и не задают расписание фоновых запусков.
|
|
||||||
Аналитика дохода зависит от полноты и свежести исходных операций.
|
|
||||||
|
|
||||||
Полностью загруженные предыдущие месяцы операций повторно читаются из T-Bank
|
|
||||||
только по кнопке «Обновить» для показанного периода. Обычный запрос догружает
|
|
||||||
отсутствующие интервалы и при необходимости обновляет текущий месяц.
|
|
||||||
Завершённые дневные свечи сохраняются; ручное обновление перечитывает
|
|
||||||
показанный диапазон. Автоматическое обнаружение поздних операций и исправлений
|
|
||||||
в уже полностью загруженных прошлых месяцах не обещается.
|
|
||||||
|
|
||||||
Кнопка «Обновить» обходит проверку давности и обновляет данные текущего
|
|
||||||
экрана: выбранного счёта, доступных счетов в режиме «Все счета» либо
|
|
||||||
инструмента и показанной личной истории на его странице. Нажатие не означает
|
|
||||||
повторную загрузку всей накопленной истории всех счетов и инструментов.
|
|
||||||
|
|
||||||
Решение охватывает все экраны с данными T-Bank. Исполненные операции и рыночная
|
|
||||||
история сохраняются; текущие позиции, остатки и цены представлены последним
|
|
||||||
успешно полученным состоянием. Будущие выплаты остаются изменяемым прогнозом,
|
|
||||||
а не исполненными операциями. Это не решение о накоплении исторических снимков
|
|
||||||
стоимости портфеля.
|
|
||||||
|
|
||||||
Рыночная история бумаг текущего портфеля загружается заранее в рамках
|
|
||||||
запрошенной пользователем работы. Для остальных бумаг первая загрузка
|
|
||||||
начинается при открытии инструмента, результат сохраняется для последующего
|
|
||||||
чтения. Предварительная загрузка истории всего каталога не требуется.
|
|
||||||
|
|
||||||
При первом подключении текущий портфель и недавние операции могут отображаться
|
|
||||||
до завершения загрузки более ранней истории. Частично загруженный период явно
|
|
||||||
отличается от полного: неполные итоги нельзя выдавать за окончательные,
|
|
||||||
а отсутствие загруженных записей — за подтверждённое отсутствие операций.
|
|
||||||
Смысл полноты определён в [глоссарии](../glossary.md#полнота-истории-счёта).
|
|
||||||
|
|
||||||
Первый запрос задаёт приоритет: сначала период, нужный экрану, включая
|
|
||||||
периоды сравнения дохода, затем вся доступная история счёта. Уже запущенная
|
|
||||||
догрузка может завершиться после ухода пользователя со страницы; нового
|
|
||||||
периодического запуска это не создаёт.
|
|
||||||
|
|
||||||
Полнота относится к успешно загруженным временным интервалам. Смена месяца
|
|
||||||
сама по себе не делает завершённым последний неполный интервал прошлого
|
|
||||||
месяца: его оставшаяся часть должна быть догружена. Ошибка промежуточной
|
|
||||||
страницы не подтверждает полноту и не удаляет ранее сохранённые данные.
|
|
||||||
Незавершённая свеча также не становится завершённой только из-за смены дня:
|
|
||||||
её окончательное состояние должен подтвердить источник.
|
|
||||||
|
|
||||||
Целевой ориентир — до 500 мс для обычного серверного запроса к заполненной БД.
|
|
||||||
Это согласованная цель измерения, а не подтверждённая характеристика системы.
|
|
||||||
Объём данных и набор запросов для проверки предстоит определить.
|
|
||||||
|
|
||||||
Для этого изменения принято допущение: ID брокерской операции не меняется
|
|
||||||
со временем. Обнаружение сменившихся ID и регулярная сверка старой истории
|
|
||||||
не входят в объём работы. Это допущение проекта, а не гарантия T-Bank:
|
|
||||||
используемый protobuf-контракт допускает изменение ID. Допущение относится
|
|
||||||
к идентичности операции; оно не требует игнорировать новые значения полей,
|
|
||||||
если операция с прежним ID снова получена от брокера.
|
|
||||||
|
|
||||||
Подключение T-Bank считается постоянным в рассматриваемом сценарии.
|
|
||||||
Архив без подключения, специальная политика хранения после отключения
|
|
||||||
и работа с закрытыми счетами пока не проектируются. Это ограничение объёма
|
|
||||||
не отменяет существующие проверки доступа и обработку ошибок интеграции.
|
|
||||||
|
|
||||||
## Alternatives
|
|
||||||
|
|
||||||
Ожидание свежего ответа T-Bank при каждом открытии экрана сохраняет зависимость
|
|
||||||
скорости и доступности интерфейса от брокера. Увеличение времени жизни кэша
|
|
||||||
в памяти уменьшает часть повторных запросов, но не сохраняет историю после
|
|
||||||
перезапуска и не обеспечивает локальное чтение всех нужных данных.
|
|
||||||
|
|
||||||
## Consequences
|
|
||||||
|
|
||||||
Интерфейсу нужны различимые состояния первичной загрузки, полноты истории,
|
|
||||||
свежести и ошибки фонового обновления. Успешная загрузка недавних операций
|
|
||||||
не подтверждает полноту более ранних периодов или периодов сравнения дохода.
|
|
||||||
|
|
||||||
Проверки локальной сессии и принадлежности личных данных сохраняются.
|
|
||||||
Способ подтверждения брокерского доступа должен позволять обслужить уже
|
|
||||||
загруженные данные без синхронного ожидания T-Bank.
|
|
||||||
|
|
||||||
Повторные запросы одного набора данных должны использовать уже выполняющуюся
|
|
||||||
загрузку. Завершённые интервалы и прогресс сохраняются в БД; после перезапуска
|
|
||||||
незавершённый интервал повторяется при следующем запросе или ручном обновлении.
|
|
||||||
Курсор брокера и максимальная дата полученной операции не доказывают полноту.
|
|
||||||
|
|
||||||
Ручное обновление периода согласует состав операций и их поля с полностью
|
|
||||||
полученным ответом источника. Только успешная загрузка всех страниц позволяет
|
|
||||||
зафиксировать полное покрытие, успешное обновление и удалить отсутствующие
|
|
||||||
в этом ответе записи периода. Ошибка не превращает частичный ответ в полный.
|
|
||||||
|
|
||||||
Личные операции и позиции сохраняют принадлежность пользователю и счёту.
|
|
||||||
Общие данные инструмента не должны содержать персональные позиции и расчёты;
|
|
||||||
поздний результат прежнего подключения не публикуется для нового подключения.
|
|
||||||
|
|
||||||
Существующие HTTP-контракты пока не поддерживают все необходимые состояния
|
|
||||||
загрузки и полноты; их изменение входит в будущую реализацию.
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
# 018. Минимальная runtime-координация
|
|
||||||
|
|
||||||
**Status:** Accepted. Уточняет cookie policy ADR 009 и prefetch policy ADR 017.
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
Изоляция владельцев данных, атомарные финансовые изменения и локальное чтение
|
|
||||||
не требуют отдельного протокола согласования каждого HTTP-ответа. Дополнительная
|
|
||||||
координация оправдана только наблюдаемым поведением, которое иначе нарушается.
|
|
||||||
|
|
||||||
## Decision
|
|
||||||
|
|
||||||
- Refresh сохраняет атомарную ротацию. Неуспешный refresh не меняет cookie:
|
|
||||||
поздний отказ не должен удалять cookie успешного запроса. Logout очищает cookie.
|
|
||||||
- Frontend объединяет refresh и упорядочивает cookie-writing requests только
|
|
||||||
внутри вкладки. Cross-tab auth synchronization не является требованием;
|
|
||||||
после изменения сессии в другой вкладке допускаются reload или re-login.
|
|
||||||
- Отменой, сбросом и публикацией query results владеет TanStack Query.
|
|
||||||
Смена session/connection очищает соответствующий cache; отдельные registries
|
|
||||||
HTTP-запросов, access epochs и URL-wide latest-read counters не используются.
|
|
||||||
Локальная auth boundary предотвращает применение старого auth result и
|
|
||||||
повтор mutation под другой identity.
|
|
||||||
- Status GET читает только состояние работы. Открытый экран перечитывает
|
|
||||||
resources через `sync=observe` во время работы и после завершения.
|
|
||||||
Сверка hashes между status и data GET, публичные progress counters и
|
|
||||||
невыдаваемые sync states не нужны. Полнота истории остаётся в resource metadata.
|
|
||||||
- Свечи загружаются для запрошенного диапазона. Предзагрузки всей истории
|
|
||||||
портфеля и автоматического расширения короткого периода до `all` нет.
|
|
||||||
- Backend формирует согласованные entries и aggregates из одних исходных
|
|
||||||
данных; frontend доверяет этому contract и не выполняет вторую сверку групп.
|
|
||||||
- Request context и HTTP completion принадлежат `nestjs-pino`/`pino-http`.
|
|
||||||
Проект сохраняет safe field filtering, redaction и route-template logging.
|
|
||||||
- Deployment всегда выполняет один stop → backup → migration → readiness
|
|
||||||
цикл. No-op и frontend-only оптимизации не обещаются; короткий простой принят.
|
|
||||||
|
|
||||||
## Consequences
|
|
||||||
|
|
||||||
Первое открытие нового диапазона свечей может ждать загрузку. Наблюдение активной
|
|
||||||
работы может перечитать неизменившиеся локальные данные, зато не требует
|
|
||||||
checksum protocol. Повтор deployment создаёт новый backup и может остановить API.
|
|
||||||
|
|
||||||
DB ownership подключения, conditional publication, coverage, staging,
|
|
||||||
банковские intent fingerprints и версии записей сохраняются. Отмена HTTP
|
|
||||||
не считается отменой уже выполняющейся server-side transaction.
|
|
||||||
Сохранённый полный forecast не заменяется candidate с изменившимися dependencies.
|
|
||||||
@@ -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.
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
# 021. Тихое чтение и редкие подтверждения
|
|
||||||
|
|
||||||
**Status:** Accepted. Заменяет manual-refresh часть
|
|
||||||
[ADR 019](019-hourly-reads-and-online-instrument-reference.md) на стороне
|
|
||||||
интерфейса и уточняет клиентскую часть
|
|
||||||
[ADR 007](007-react-query-fetch-policy.md). Не меняет владельцев обновления
|
|
||||||
и границы чтения из ADR 019.
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
Экраны просмотра перекладывали на пользователя контроль над получением данных:
|
|
||||||
кнопки «Обновить» и «Повторить», индикатор фонового обновления и сообщение
|
|
||||||
о неудачной синхронизации повторялись в разных разделах. Пользователь приходит
|
|
||||||
смотреть финансы, а не обслуживать загрузку; восстановление после временного
|
|
||||||
сбоя не должно зависеть от нажатия кнопки.
|
|
||||||
|
|
||||||
При этом состояние самих данных остаётся значимым: время последнего успешного
|
|
||||||
результата источника, устаревание и исход часовой попытки влияют на доверие
|
|
||||||
к показанным суммам и не могут быть просто скрыты.
|
|
||||||
|
|
||||||
## Decision
|
|
||||||
|
|
||||||
- Автоматическое чтение клиента не сообщает о себе. Экраны просмотра не
|
|
||||||
показывают кнопок ручного обновления и повтора, индикаторов фонового
|
|
||||||
перечитывания и технических предупреждений о собственном запросе.
|
|
||||||
- Временные сбои GET восстанавливаются сами: ограниченный срок ожидания запроса
|
|
||||||
и не более двух повторов с возрастающими паузами, затем возврат к штатной
|
|
||||||
работе при появлении результата. Пользователь не инициирует восстановление.
|
|
||||||
- Состояние источника из `meta` остаётся видимым. Время последнего успешного
|
|
||||||
результата, признак устаревания, исход последней часовой попытки и неполнота
|
|
||||||
показываются текстом либо доступно именованным элементом, а не только цветом.
|
|
||||||
Это свойство данных, а не отчёт о техническом процессе, и оно не превращается
|
|
||||||
в действие пользователя.
|
|
||||||
- Доступный результат сохраняется при обновлении и временной ошибке. Неполнота
|
|
||||||
и подтверждённая пустота остаются различимыми: неизвестная сумма не становится
|
|
||||||
нулём, незавершённый расчёт не выдаётся за окончательный, незагруженный список
|
|
||||||
не выдаётся за отсутствие записей.
|
|
||||||
- Первичное ожидание ограничено по времени и показывает структуру будущего
|
|
||||||
содержимого. По его истечении экран переходит в спокойную локальную
|
|
||||||
недоступность вместо бесконечной загрузки и сам заменяет её содержимым после
|
|
||||||
восстановления. Сбой одного блока не скрывает остальные доступные блоки.
|
|
||||||
- Ошибки явных действий пользователя сохраняются и остаются видимыми. Решение
|
|
||||||
касается только чтения; отправка формы, изменение данных и сброс фильтра
|
|
||||||
по-прежнему сообщают результат, в том числе неуспешный.
|
|
||||||
|
|
||||||
## Consequences
|
|
||||||
|
|
||||||
Пользователь не может ускорить обновление вручную: нормальным владельцем
|
|
||||||
обновления остаётся часовой обход из ADR 019, и отсутствие кнопки не меняет его
|
|
||||||
срок. Если источник недоступен долго, экран показывает прежние данные с
|
|
||||||
обозначением устаревания, а не приглашение повторить.
|
|
||||||
|
|
||||||
Автоматический повтор ограничен: он покрывает короткие сетевые сбои, но не
|
|
||||||
длительную недоступность. Ошибки доступа, домена и отмены не повторяются,
|
|
||||||
mutations не повторяются вовсе.
|
|
||||||
|
|
||||||
Состояние источника и состояние клиентского чтения описывают разные вещи и
|
|
||||||
не подменяют друг друга. `Portfolio.isStale` сохраняет смысл возраста снимка;
|
|
||||||
ошибка клиентского перечитывания его не меняет.
|
|
||||||
|
|
||||||
Детали поведения — в
|
|
||||||
[ожидании и восстановлении интерфейса](../architecture/cross-cutting/ui-resilience.md)
|
|
||||||
и [свежести данных](../architecture/cross-cutting/data-freshness.md).
|
|
||||||
Согласованные требования принадлежат workspace-спецификациям
|
|
||||||
`frontend-user-feedback`, `frontend-data-resilience` и
|
|
||||||
`frontend-loading-presentation`.
|
|
||||||
+3
-8
@@ -17,12 +17,7 @@
|
|||||||
- [007 — React Query fetch policy](007-react-query-fetch-policy.md)
|
- [007 — React Query fetch policy](007-react-query-fetch-policy.md)
|
||||||
- [008 — Pino backend logging](008-pino-backend-logging.md)
|
- [008 — Pino backend logging](008-pino-backend-logging.md)
|
||||||
- [009 — Concurrent refresh rotation policy](009-concurrent-refresh-rotation-policy.md)
|
- [009 — Concurrent refresh rotation policy](009-concurrent-refresh-rotation-policy.md)
|
||||||
|
- [010 — Native OpenSpec SDD и local orchestration](010-native-openspec-sdd-and-local-orchestration.md) — исторический; частично заменён ADR 011, обязательность SDD отменена ADR 012.
|
||||||
|
- [011 — Native OpenSpec lifecycle и repository gates](011-native-openspec-lifecycle-with-repository-gates.md) — заменён ADR 012 в части обязательного lifecycle.
|
||||||
|
- [012 — Прямая разработка с контекстом по задаче](012-task-driven-development-with-scoped-context.md)
|
||||||
- [013 — НКР как первый источник кредитных рейтингов облигаций](013-nkr-bond-credit-ratings.md)
|
- [013 — НКР как первый источник кредитных рейтингов облигаций](013-nkr-bond-credit-ratings.md)
|
||||||
- [014 — Актуальная OpenAPI-схема как вход frontend](014-trusted-openapi-input.md)
|
|
||||||
- [015 — SQLite в ограниченном single-server deployment](015-single-server-sqlite-deployment.md)
|
|
||||||
- [016 — Версионируемый комплект развёртывания](016-versioned-deployment-bundle.md)
|
|
||||||
- [017 — Локальное чтение данных T-Bank и фоновая синхронизация](017-local-tbank-reads-and-background-sync.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)
|
|
||||||
- [021 — Тихое чтение и редкие подтверждения](021-quiet-reading-and-rare-confirmations.md)
|
|
||||||
|
|||||||
@@ -15,8 +15,7 @@
|
|||||||
| Что считается кэшем и когда прежние данные теряют право на повторное использование? | [Свежесть данных](cross-cutting/data-freshness.md) |
|
| Что считается кэшем и когда прежние данные теряют право на повторное использование? | [Свежесть данных](cross-cutting/data-freshness.md) |
|
||||||
| Как обрабатываются ошибки и ожидание? | [HTTP API и ошибки](http-api.md), [состояния интерфейса](cross-cutting/ui-resilience.md) |
|
| Как обрабатываются ошибки и ожидание? | [HTTP API и ошибки](http-api.md), [состояния интерфейса](cross-cutting/ui-resilience.md) |
|
||||||
| Как связать ошибку с обработкой запроса? | [Наблюдаемость](http-api.md#наблюдаемость) |
|
| Как связать ошибку с обработкой запроса? | [Наблюдаемость](http-api.md#наблюдаемость) |
|
||||||
| Как устроено подтверждённое локальное окружение? | [Container](containers.md#представление-container), [качество и ограничения](containers.md#качество-и-ограничения) |
|
| Как устроено подтверждённое локальное окружение? | [Container](containers.md#представление-container), [локальная диагностика](../development/local-workspace.md) |
|
||||||
| Как запустить, обновить или восстановить приложение? | [Развёртывание](deployment.md) |
|
|
||||||
| Что означают предметные термины? | [Глоссарий](../glossary.md) |
|
| Что означают предметные термины? | [Глоссарий](../glossary.md) |
|
||||||
|
|
||||||
Эта карта покрывает вопросы архитектуры там, где о них есть подтверждённое
|
Эта карта покрывает вопросы архитектуры там, где о них есть подтверждённое
|
||||||
|
|||||||
+35
-105
@@ -27,57 +27,18 @@ flowchart LR
|
|||||||
F -->|"HTTPS; изображения без авторизации"| C
|
F -->|"HTTPS; изображения без авторизации"| C
|
||||||
```
|
```
|
||||||
|
|
||||||
SPA выполняется в браузере пользователя. Backend — отдельное
|
SPA выполняется в браузере пользователя. Backend — отдельное локальное
|
||||||
приложение; SQLite используется внутри его процесса через библиотеку и файл
|
приложение; SQLite используется внутри его процесса через библиотеку и файл
|
||||||
базы. Между API и SQLite нет сетевого database server. Граница системы на
|
базы. Между API и SQLite нет сетевого database server. Граница системы на
|
||||||
схеме показывает принадлежность частей, а не их размещение на одном узле.
|
схеме показывает принадлежность частей, а не их размещение на одном узле.
|
||||||
|
|
||||||
## Контейнерная поставка
|
|
||||||
|
|
||||||
Deployment view для одного сервера. Caddy принадлежит серверу; Compose
|
|
||||||
содержит два постоянных application containers и одноразовую migration.
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
flowchart LR
|
|
||||||
Browser["Браузер · React SPA"] -->|HTTPS| Caddy["Серверный Caddy · TLS"]
|
|
||||||
subgraph Host["Один Docker host"]
|
|
||||||
Caddy -->|"HTTP · 127.0.0.1:8080"| Frontend["frontend · nginx · SPA + /api/"]
|
|
||||||
Frontend -->|"HTTP /api/v1 · internal network"| Backend["backend · NestJS · один process"]
|
|
||||||
Migration["migration · Prisma · one-shot"] -->|"До старта backend"| Data[("Host directory · SQLite + WAL")]
|
|
||||||
Backend -->|"Чтение и запись /data"| Data
|
|
||||||
Secrets["Restricted environment · ключи вне images"] --> Backend
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Только frontend публикует host port, привязанный к loopback. Backend не
|
|
||||||
публикует порт, но имеет исходящий доступ к внешним API. nginx сохраняет
|
|
||||||
`/api/` prefix, forwarding headers и request ID; Docker DNS разрешается
|
|
||||||
повторно после backend recreation. HTML требует revalidation, hashed assets
|
|
||||||
имеют immutable cache. Caddy не содержит отдельного маршрута к API.
|
|
||||||
|
|
||||||
Development заменяет nginx на Vite с HMR, backend — на Nest watch, а host data
|
|
||||||
path — на отдельный named volume. Browser origin остаётся единым. Linux
|
|
||||||
node_modules не монтируются с хоста. Production SPA собирается с relative API
|
|
||||||
base; production cookies остаются Secure, публичный origin использует HTTPS.
|
|
||||||
|
|
||||||
Компоненты имеют независимые images и версии. Production фиксирует image
|
|
||||||
version tags; migration использует тот же backend image. Успех migration предшествует
|
|
||||||
backend healthcheck на существующем auth route; frontend ждёт healthy backend.
|
|
||||||
Runtime работает non-root, SQLite сохраняется вне image, ключ шифрования и JWT
|
|
||||||
secret поступают из restricted environment и сохраняются между обновлениями.
|
|
||||||
|
|
||||||
Ограничение SQLite расширено в [ADR 015](../adr/015-single-server-sqlite-deployment.md).
|
|
||||||
[Runbook](deployment.md) описывает установку, independent
|
|
||||||
updates, backup, rollback и explicit restore. Он также перечисляет внешние
|
|
||||||
prerequisites: локальный smoke не подтверждает registry/server rollout.
|
|
||||||
|
|
||||||
## Ответственность частей
|
## Ответственность частей
|
||||||
|
|
||||||
| Часть | Ответственность |
|
| Часть | Ответственность |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| React SPA | Экраны, навигация, формы, состояние входа и клиентский кэш ответов; форматирование денежных значений |
|
| React SPA | Экраны, навигация, формы, состояние входа и клиентский кэш ответов; форматирование денежных значений |
|
||||||
| Backend API | Локальная идентичность, сессии, проверка доступа, подключение брокера, нормализация внешних данных, точные расчёты и синхронизация сохранённых источников |
|
| Backend API | Локальная идентичность, сессии, проверка доступа, подключение брокера, нормализация внешних данных, точные расчёты и серверные кэши |
|
||||||
| SQLite | Долговременное хранение локальных пользователей, сессий, зашифрованных подключений, ручной банковской истории и инвестиционных источников с coverage/work |
|
| SQLite | Долговременное хранение локальных пользователей, сессий, зашифрованных подключений и ручной банковской истории |
|
||||||
| Внешние системы | Данные брокера, публичные сведения об инструментах и изображения в пределах своей ответственности |
|
| Внешние системы | Данные брокера, публичные сведения об инструментах и изображения в пределах своей ответственности |
|
||||||
|
|
||||||
Frontend организован слоями `app → pages → features → entities → shared`:
|
Frontend организован слоями `app → pages → features → entities → shared`:
|
||||||
@@ -86,32 +47,16 @@ Frontend организован слоями `app → pages → features → ent
|
|||||||
где локальная аутентификация отделена от подключения брокера и инвестиционных
|
где локальная аутентификация отделена от подключения брокера и инвестиционных
|
||||||
сценариев. Это внутренняя организация приложений, не набор микросервисов.
|
сценариев. Это внутренняя организация приложений, не набор микросервисов.
|
||||||
|
|
||||||
Маршруты и AppShell принадлежат `app`. Page controller/VM управляет запросами,
|
|
||||||
формой и восстановлением; presentation получает данные и callbacks, поля
|
|
||||||
могут использовать контекст React Hook Form. В OperationForm controller
|
|
||||||
сохраняет intent операции, черновик и явное принятие актуальной версии после
|
|
||||||
конфликта. В account-income чистые преобразования периода принадлежат модели
|
|
||||||
страницы и не связывают форму с её запросами.
|
|
||||||
|
|
||||||
Локальные packages tokens/ui/icons образуют [визуальную основу](../design/overview.md#общая-основа-и-каталог)
|
|
||||||
внутри frontend и входят в сборку SPA. Это библиотеки, а не дополнительные
|
|
||||||
runtime-контейнеры. Провайдеры темы принадлежат приложению и Storybook;
|
|
||||||
предметные компоненты и API-клиент остаются в приложении. Отдельный API-пакет
|
|
||||||
и registry-поставка визуальных пакетов не реализованы. Существующие Docker
|
|
||||||
images приложения не означают независимого npm-выпуска этих библиотек.
|
|
||||||
|
|
||||||
Финансовые HTTP-запросы браузера идут через backend. Пользователь вводит token
|
Финансовые HTTP-запросы браузера идут через backend. Пользователь вводит token
|
||||||
T-Bank в форме и отправляет его для сохранения, но API не возвращает token
|
T-Bank в форме и отправляет его для сохранения, но API не возвращает token
|
||||||
в ответах; SPA не использует его для прямых брокерских запросов. Прямая
|
в ответах; SPA не использует его для прямых брокерских запросов. Прямая
|
||||||
загрузка публичного изображения из CDN не проходит через эту финансовую
|
загрузка публичного изображения из CDN не проходит через эту финансовую
|
||||||
границу.
|
границу.
|
||||||
|
|
||||||
Типизированный frontend-клиент производится из предоставленного OpenAPI snapshot
|
Типизированный frontend-клиент производится из OpenAPI backend во время
|
||||||
во время разработки. В runtime SPA вызывает API, а не генератор или OpenAPI
|
разработки. В runtime SPA вызывает API, а не генератор или OpenAPI endpoint.
|
||||||
endpoint. Форма запросов и ошибок описана в [HTTP API](http-api.md); направление
|
Форма запросов и ошибок описана в [HTTP API](http-api.md); направление
|
||||||
генерации принято в [ADR 002](../adr/002-nest-generated-openapi-contract.md),
|
генерации принято в [ADR 002](../adr/002-nest-generated-openapi-contract.md).
|
||||||
а доверие frontend к предоставленному snapshot — в
|
|
||||||
[ADR 014](../adr/014-trusted-openapi-input.md).
|
|
||||||
|
|
||||||
## Хранимые данные и границы доступа
|
## Хранимые данные и границы доступа
|
||||||
|
|
||||||
@@ -134,41 +79,28 @@ credentials не являются сохраняемым представлен
|
|||||||
или перезапуска. Архив обратим и сохраняет историю. Подробности —
|
или перезапуска. Архив обратим и сохраняет историю. Подробности —
|
||||||
[банковские карты](../domains/cards/overview.md).
|
[банковские карты](../domains/cards/overview.md).
|
||||||
|
|
||||||
Сессии и подключение связаны с владельцем. Backend локально проверяет
|
Сессии и подключение связаны с владельцем. Access token подтверждает
|
||||||
membership текущей revision перед каждым финансовым чтением. Unknown membership
|
локального пользователя, но `accountId` из URL сам по себе не даёт права
|
||||||
не открывает доступ к произвольному счёту; известный отказ токена блокирует
|
читать брокерский счёт. Backend получает текущее подключение пользователя
|
||||||
личные данные. Ротация refresh token остаётся атомарной.
|
и его generation перед использованием финансового кэша. Проверка доступности
|
||||||
|
счёта у брокера зависит от сценария: журнал выполняет её до чтения страницы
|
||||||
|
кэша; свежий портфель одного счёта может быть возвращён без нового запроса
|
||||||
|
списка счетов. При обновлении портфеля доступ проверяется у брокера.
|
||||||
|
|
||||||
SQLite хранит полный последний portfolio, операции и их подтверждённые
|
Ротация сессии атомарно заменяет hash текущего refresh token: два
|
||||||
диапазоны, private прогнозы, сохранённые точки истории оценки, общие
|
конкурентных запроса не могут оба успешно выполнить одну ротацию.
|
||||||
instrument/market sources и intent/staging.
|
Вызов брокера и запись в локальную базу не образуют общую транзакцию:
|
||||||
Нормализованные финансовые значения сохраняются decimal-строками. Полнота
|
при сохранении подключения сначала проверяется token, затем записывается
|
||||||
отделена от свежести и пагинации. Ответ не ждёт внешних RPC; обновлением
|
шифротекст. Подробности находятся в
|
||||||
личных данных владеет часовой обход в том же backend process через
|
[аутентификации](../domains/authentication/overview.md) и
|
||||||
существующий лимитер, недостающие общие источники он же ставит в фоновую
|
[интеграции T-Bank](../integrations/tbank.md).
|
||||||
очередь. На startup обход начинает следующую попытку по обычному часовому
|
|
||||||
сроку; после нового подключения первая попытка начинается сразу.
|
|
||||||
Подробности — [модель свежести](cross-cutting/data-freshness.md).
|
|
||||||
|
|
||||||
PUT подключения сначала проверяет token у T-Bank, затем одной транзакцией
|
Брокерские счета, сводки, портфель, страницы журнала и будущие события
|
||||||
сохраняет encrypted token, новую revision и проверенный membership.
|
не являются долговременной историей в SQLite. Они получаются для текущего
|
||||||
Сетевая операция не удерживает SQLite-транзакцию. Общие источники не содержат
|
подключения и могут временно кэшироваться. Перезапуск backend не
|
||||||
личных позиций, ожидаемых сумм или credentials. Произвольного архива
|
восстанавливает прежние финансовые снимки и cursor continuations из базы;
|
||||||
отключённых счетов нет.
|
[модель свежести](cross-cutting/data-freshness.md) отделяет это от хранения
|
||||||
|
учётных данных.
|
||||||
## Сохранение и восстановление данных
|
|
||||||
|
|
||||||
Поддерживаемая процедура останавливает backend и копирует весь data directory
|
|
||||||
с возможными WAL/SHM files до migration. Копирование отдельных файлов
|
|
||||||
работающей базы не сохраняет целостный снимок; для live backup потребовался бы
|
|
||||||
SQLite backup API. Восстановление проверяется на отдельной копии. Ключ шифрования подключения хранится отдельно от базы;
|
|
||||||
его потеря делает сохранённый T-Bank token нечитаемым.
|
|
||||||
|
|
||||||
Откат приложения требует совместимости предыдущей версии с сохранённой
|
|
||||||
схемой. Расширенная банковскими таблицами схема сохраняет пользователей,
|
|
||||||
сессии и подключения. Возврат старого backup поверх новой истории удалил бы
|
|
||||||
введённые банковские операции; откат приложения не равнозначен откату данных.
|
|
||||||
Совместимость произвольных будущих версий этим не гарантируется.
|
|
||||||
|
|
||||||
## Стратегия решения и её основания
|
## Стратегия решения и её основания
|
||||||
|
|
||||||
@@ -198,18 +130,16 @@ access token и вращаемый refresh token. [Политика query-кэш
|
|||||||
|
|
||||||
Авторизация доступа, недопущение token в публичный ответ и атомарность
|
Авторизация доступа, недопущение token в публичный ответ и атомарность
|
||||||
ротации сессии — отдельные свойства; наличие типизированного клиента
|
ротации сессии — отдельные свойства; наличие типизированного клиента
|
||||||
не доказывает их. Действующий формат ошибок описан в
|
не доказывает их. Известные расхождения регистрации и деталей валидации
|
||||||
[HTTP API](http-api.md#error-contract).
|
отмечены в [аутентификации](../domains/authentication/overview.md#регистрация-и-вход)
|
||||||
|
и [HTTP API](http-api.md#error-contract).
|
||||||
|
|
||||||
Поддерживается локальный Compose и ограниченная single-server topology с SQLite.
|
Подтверждённая топология — локальная SPA и локальный API с файлом SQLite.
|
||||||
Origin браузера, адрес API и cookie policy должны быть согласованы.
|
Origin браузера, адрес API и cookie policy должны быть согласованы; условия
|
||||||
`localhost` и `127.0.0.1` задают разные browser-границы для cookie и CORS.
|
и диагностика описаны в [локальном окружении](../development/local-workspace.md).
|
||||||
Локальный HTTP поддерживается в development-режиме backend; в остальных
|
Наличие локального запуска не задаёт production-топологию или гарантии SLA.
|
||||||
режимах refresh-cookie имеет Secure flag и требует защищённого соединения.
|
|
||||||
Single-server deployment не задаёт гарантий HA или zero-downtime.
|
|
||||||
|
|
||||||
Coordinator и лимитер T-Bank действуют на один экземпляр backend. Сохранённые
|
Кэши и лимитер T-Bank действуют на экземпляр backend. Ротация ключа шифрования
|
||||||
данные переживают перезапуск; processRunId не является distributed lease. Ротация ключа шифрования
|
|
||||||
подключений и согласование изменений подключения между распределёнными
|
подключений и согласование изменений подключения между распределёнными
|
||||||
экземплярами не входят в реализованный механизм. Эти ограничения не
|
экземплярами не входят в реализованный механизм. Эти ограничения не
|
||||||
позволяют переносить локальную схему на несколько экземпляров без отдельного
|
позволяют переносить локальную схему на несколько экземпляров без отдельного
|
||||||
|
|||||||
@@ -1,91 +1,72 @@
|
|||||||
# Свежесть данных и повторное использование
|
# Свежесть данных и повторное использование
|
||||||
|
|
||||||
WealthLedger читает личные инвестиционные данные из SQLite. Обычный GET и его
|
WealthLedger показывает финансовое наблюдение на момент получения данных.
|
||||||
HTTP retry возвращают сохранённый результат и не запускают личную работу с
|
Временный кэш уменьшает повторные обращения к брокеру, но не превращает
|
||||||
T-Bank. Нормальным владельцем обновления служит часовой обход текущего
|
снимки в историю портфеля и не обещает рыночные данные в реальном времени.
|
||||||
подключения: после успешного подключения он начинает первую попытку, затем
|
|
||||||
начинает следующую не ранее чем через 60 минут от начала предыдущей. После
|
|
||||||
простоя выполняется не более одна актуальная попытка, а перекрывающийся срок
|
|
||||||
пропускается, не ставится в очередь. Общие справочные данные инструментов
|
|
||||||
запрашиваются онлайн, когда это требуется карточке; финансовый HTTP-ответ не
|
|
||||||
ждёт T-Bank. Модель принята в
|
|
||||||
[ADR 019](../../adr/019-hourly-reads-and-online-instrument-reference.md).
|
|
||||||
|
|
||||||
## Клиентский и серверный снимки
|
## Клиентский и серверный снимки
|
||||||
|
|
||||||
[Client Cached Snapshot](../../glossary.md#client-cached-snapshot) — успешный
|
[Client Cached Snapshot](../../glossary.md#client-cached-snapshot) — последний
|
||||||
ответ в TanStack Query. Он сохраняет показанное содержимое при временной ошибке
|
успешный ответ, сохранённый TanStack Query в SPA. При обычном фоновом обновлении
|
||||||
HTTP-перечитывания в той же границе доступа. Это не серверный fallback и не
|
того же набора данных уже показанный результат остаётся на экране; ошибка
|
||||||
состояние часового обхода. SQLite независимо от браузера хранит membership,
|
обновления сообщает о проблеме, не заменяя известные значения пустотой.
|
||||||
последний полный portfolio, исполненные операции, прогнозы, общие сведения об
|
Клиентский кэш не является внешней системой или отдельным runtime-приложением.
|
||||||
инструментах, котировки, свечи и календари.
|
|
||||||
|
|
||||||
[Полнота периода](../../glossary.md#полнота-истории-счёта) и свежесть различаются.
|
[Portfolio Snapshot](../../glossary.md#portfolio-snapshot) — полный результат
|
||||||
Успешно проверенный пустой результат полон и доступен; последняя страница
|
загрузки портфеля на backend. В пределах разрешённого сценарием срока он может
|
||||||
журнала сама по себе полноту не доказывает. Результат каждого личного ресурса
|
служить серверным fallback при временной ошибке upstream. Backend отмечает
|
||||||
в `{ data, meta }` описывают пять независимых значений: freshness,
|
такой ответ `isStale`; frontend не выводит это поле из состояния собственного
|
||||||
completeness, `lastSuccessfulAt`, `lastAttempt` и availability. Первые,
|
query-кэша. Серверное предупреждение и ошибка клиентского refetch могут
|
||||||
полнота и `lastSuccessfulAt` относятся только к возвращённому подтверждённому
|
возникать независимо.
|
||||||
результату. `lastAttempt` описывает относящуюся к ресурсу часовую попытку:
|
|
||||||
`none`, `running`, `succeeded`, `partial` или `failed`.
|
|
||||||
|
|
||||||
Итог попытки выводится из фактических результатов её шагов: попытка, не
|
Свежесть и допустимость fallback определяются сценарием. Портфельный fallback
|
||||||
завершившая обязательный шаг, не обозначается полным успехом, а полученные
|
не распространяется автоматически на журнал, прогноз событий или страницу
|
||||||
успешные результаты сохраняются. Ожидание каждого шага ограничено; зависший
|
инструмента. Точные правила находятся в
|
||||||
шаг учитывается проблемным, а начатое обращение к T-Bank не отменяется.
|
[справочнике портфеля](../../domains/portfolio/data-reference.md),
|
||||||
Попытка, потерявшая выполнение после сбоя сохранения или остановки процесса,
|
[журнале операций](../../domains/operations/overview.md) и
|
||||||
закрывается частичным успехом либо неуспехом и не блокирует следующий обход
|
[будущих событиях](../../domains/income/upcoming-events.md).
|
||||||
дольше обычного часового срока.
|
|
||||||
|
|
||||||
Поздняя неуспешная или частичная попытка сохраняет прежний полный результат и
|
По [ADR 007](../../adr/007-react-query-fetch-policy.md) запрос связан с открытием
|
||||||
его исходное время. Если полного результата нет, `data` равно `null`: состояние
|
экрана, а возврат фокуса окна не запускает refetch. Повторное открытие после
|
||||||
availability объясняет первоначальную загрузку, временную недоступность либо
|
`staleTime` может обновить ответ в фоне. Эта политика не является таймером
|
||||||
недоступность подключения. Пустой массив не означает отсутствие результата.
|
периодического обновления. Перезапуск backend уничтожает его in-memory
|
||||||
Старый полный portfolio не удаляется из-за возраста. `Portfolio.isStale`
|
снимки и continuations журнала; SQLite не восстанавливает их.
|
||||||
сохраняет смысл возраста его snapshot, а не обозначает неуспех обхода или
|
|
||||||
клиентскую ошибку перечитывания.
|
|
||||||
|
|
||||||
| Источник | Владелец нормального обновления |
|
|
||||||
| --- | --- |
|
|
||||||
| Membership, portfolio, изменяемый хвост операций и события | Часовой обход текущего подключения |
|
|
||||||
| Котировки и изменяемый хвост дневных свечей | Установленная политика источника; не ordinary personal GET |
|
|
||||||
| Metadata, поиск каталога и company | Онлайн при чтении |
|
|
||||||
| Полные прошлые интервалы операций и завершённые свечи | Не обновляются часовым обходом или обычным GET |
|
|
||||||
|
|
||||||
Два исключения разрешают только первое получение отсутствующего результата:
|
|
||||||
первое открытие неподтверждённого исторического интервала операций получает
|
|
||||||
только этот интервал и возвращает локальные строки с неполным покрытием; первое
|
|
||||||
открытие диапазона свечей без подтверждённого локального результата получает
|
|
||||||
только этот диапазон. Ни одно исключение не обновляет membership, portfolio,
|
|
||||||
события, другой интервал или диапазон. Их HTTP retry также только перечитывает
|
|
||||||
локальное состояние. Подтверждённый полный интервал или диапазон остаётся
|
|
||||||
локальным.
|
|
||||||
|
|
||||||
Часовой обход сохраняет успешные разделы независимо, а failed section не
|
|
||||||
заменяет прежний complete result. Отменой query lifecycle владеет TanStack
|
|
||||||
Query. Открытый экран перечитывает ресурс не чаще одного раза в 30 минут;
|
|
||||||
это локальный HTTP read. Совместимый параметр `sync=observe`, пока он есть в
|
|
||||||
маршруте, также является только чтением и не представляет команду обходу.
|
|
||||||
[HTTP API](../http-api.md) описывает исполняемый контракт envelope.
|
|
||||||
|
|
||||||
## Смена подключения
|
## Смена подключения
|
||||||
|
|
||||||
Каждое чтение проверяет локальную сессию, текущее подключение, известный отказ
|
Кэш финансовых данных относится к пользователю, счёту и текущему
|
||||||
токена и сохранённый состав доступных счетов. Расшифровка token и GetAccounts
|
подключению T-Bank. Backend сначала читает подключение и его generation, затем
|
||||||
для локальной выдачи не нужны. Старый membership допускает выдачу и фоновую
|
проверяет возможность повторного использования ответа. Свежий портфель
|
||||||
сверку; это задержка обнаружения отзыва доступа, а не live-проверка брокера.
|
одного счёта не требует нового запроса списка счетов у брокера; при обновлении
|
||||||
Unknown membership не даёт доступа к произвольному accountId: ответ сообщает
|
портфеля доступ проверяется заново. Журнал операций проверяет доступ до чтения
|
||||||
о загрузке доступа. Успешный новый список немедленно ограничивает последующие
|
кэшированной страницы. Идентификатор счёта и совпадение параметров запроса
|
||||||
чтения. Известный invalid token блокирует личные данные.
|
сами по себе недостаточны для выдачи кэша.
|
||||||
|
|
||||||
Успешный PUT атомарно сохраняет token, новую случайную внутреннюю revision
|
При каждом успешном сохранении подключения token шифруется с новым
|
||||||
и membership из проверки token. Прежние личные datasets и work становятся
|
AES-GCM nonce. Из сохранённого шифротекста backend вычисляет непрозрачную
|
||||||
недоступны. DELETE удаляет подключение и личные данные; общие неперсональные
|
внутреннюю generation. Она не хранится отдельным полем базы и не входит
|
||||||
источники могут оставаться. Неуспешный PUT сохраняет действующую границу.
|
в API, журналы или ошибки. Portfolio Snapshot и Ledger Page Cache допускают
|
||||||
|
повторное использование только в пределах соответствующей generation.
|
||||||
|
Старый кэш или поздно завершившийся запрос прежней generation не обслуживает
|
||||||
|
новый запрос после замены подключения, включая сохранение того же plaintext
|
||||||
|
token. Удаление подключения не оставляет доступного контекста для чтения кэша.
|
||||||
|
|
||||||
Публикация проверяет revision и attempt в той же SQLite-транзакции; поздняя
|
После успешного изменения подключения frontend сначала записывает
|
||||||
работа старого подключения не меняет даже timestamp. Revision не попадает
|
подтверждённый status, затем сбрасывает generated GET queries в поддеревьях
|
||||||
в API, логи или cursor. Frontend очищает personal queries при смене подключения
|
`/api/v1/brokerage-accounts`, `/api/v1/stocks` и `/api/v1/bonds`. Активные
|
||||||
и известном отказе доступа; поздний ответ не восстанавливает скрытые данные.
|
выборки запрашиваются заново, неактивные — при следующем использовании.
|
||||||
Logout очищает сессию и query cache. Подробнее —
|
Client Cached Snapshot прежнего подключения больше не служит результатом
|
||||||
[интеграция T-Bank](../../integrations/tbank.md).
|
новой границы.
|
||||||
|
|
||||||
|
Неуспешная операция подключения не меняет статус, generation и доступные
|
||||||
|
финансовые данные. Обычная ошибка refetch также сохраняет клиентский снимок
|
||||||
|
текущей границы. Logout — отдельное событие: оно очищает состояние
|
||||||
|
пользовательской сессии, status подключения и query cache.
|
||||||
|
|
||||||
|
Пользовательский сценарий описан в [интеграции T-Bank](../../integrations/tbank.md).
|
||||||
|
Согласованные правила frontend находятся в workspace-спецификациях
|
||||||
|
[query coherence](../../../specs/openspec/specs/frontend-query-coherence/spec.md)
|
||||||
|
и [data resilience](../../../specs/openspec/specs/frontend-data-resilience/spec.md);
|
||||||
|
серверные правила — в
|
||||||
|
[portfolio](../../../specs/openspec/specs/portfolio-view/spec.md) и
|
||||||
|
[ledger](../../../specs/openspec/specs/brokerage-account-ledger/spec.md).
|
||||||
|
|||||||
@@ -16,48 +16,20 @@
|
|||||||
пока неизвестно, доступен public или protected маршрут. При загрузке
|
пока неизвестно, доступен public или protected маршрут. При загрузке
|
||||||
отложенного маршрута уже доступный каркас остаётся видимым.
|
отложенного маршрута уже доступный каркас остаётся видимым.
|
||||||
|
|
||||||
Фоновое чтение не сообщает о себе: оно сохраняет видимые данные без индикации
|
Фоновое обновление и действие пользователя сохраняют видимые данные или
|
||||||
процесса и без технического предупреждения. В частности,
|
элемент управления с компактной индикацией процесса. В частности,
|
||||||
[клиентский снимок](data-freshness.md#клиентский-и-серверный-снимки) счетов или
|
[клиентский снимок](data-freshness.md#клиентский-и-серверный-снимки) счетов или
|
||||||
портфеля остаётся доступным после ошибки refetch, и эта ошибка не означает
|
портфеля остаётся доступным после ошибки refetch; сообщение и повтор
|
||||||
отсутствие инвестиций. Действие пользователя сохраняет видимые данные или
|
относятся к обновлению, а не к отсутствию инвестиций.
|
||||||
элемент управления и сообщает свой результат, включая неуспешный.
|
|
||||||
|
|
||||||
Первичное ожидание ограничено по времени. По его истечении область переходит
|
|
||||||
в спокойную локальную недоступность вместо бесконечной загрузки и сама
|
|
||||||
заменяет её содержимым после восстановления. Решение принято в
|
|
||||||
[ADR 021](../../adr/021-quiet-reading-and-rare-confirmations.md).
|
|
||||||
|
|
||||||
Серверная полнота не выводится из query lifecycle. Missing показывает
|
|
||||||
skeleton только отсутствующих секций, partial — подтверждённые строки и
|
|
||||||
сообщение о неполноте. Totals ждут обязательных зависимостей, comparisons
|
|
||||||
готовятся независимо. Только complete empty означает отсутствие операций.
|
|
||||||
Время источника не заменяется временем получения HTTP-ответа.
|
|
||||||
|
|
||||||
Экраны просмотра не предлагают ручное обновление или повтор чтения: временные
|
|
||||||
сбои GET восстанавливаются сами ограниченным числом повторов, сохраняя
|
|
||||||
показанный результат и applied filters. Ошибка transport отличается от
|
|
||||||
ошибки source work. Status observation прекращается после terminal outcome
|
|
||||||
или ухода; само истечение TTL не запускает импорт. Известный access loss
|
|
||||||
очищает personal view, включая защиту от позднего ответа.
|
|
||||||
|
|
||||||
Состояние источника при этом видимо: время последнего успешного результата,
|
|
||||||
устаревание, исход последней часовой попытки и неполнота показаны текстом либо
|
|
||||||
доступно именованным элементом рядом с данными, к которым относятся. Это
|
|
||||||
свойство данных, а не отчёт о техническом процессе, и оно не требует действия.
|
|
||||||
|
|
||||||
## Известная ошибка сценария
|
## Известная ошибка сценария
|
||||||
|
|
||||||
Ошибки счетов, портфеля и подключения T-Bank остаются локальными: затронутый
|
Ошибки счетов, портфеля и подключения T-Bank остаются локальными: рядом
|
||||||
блок показывает собственное состояние и не скрывает успешно отображённые
|
с затронутыми данными доступны предусмотренные сценарием retry и navigation
|
||||||
соседние блоки. Устаревший серверный Portfolio Snapshot показывает сохранённый
|
actions. Устаревший серверный Portfolio Snapshot показывает сохранённый
|
||||||
портфель с обозначением устаревания. Он не превращается в глобальный fallback.
|
портфель с предупреждением. Он не превращается в глобальный fallback.
|
||||||
Ошибка источника не означает нулевой баланс или отсутствие операций.
|
Ошибка источника не означает нулевой баланс или отсутствие операций.
|
||||||
|
|
||||||
Предусмотренные сценарием navigation actions сохраняются: подтверждённая потеря
|
|
||||||
доступа предлагает восстановить подключение, а `404` — вернуться к списку
|
|
||||||
счетов. Это навигация и явные действия пользователя, а не повтор чтения.
|
|
||||||
|
|
||||||
## Непойманная ошибка маршрута
|
## Непойманная ошибка маршрута
|
||||||
|
|
||||||
Непойманная runtime-ошибка при отображении public или protected route
|
Непойманная runtime-ошибка при отображении public или protected route
|
||||||
@@ -72,6 +44,6 @@ skeleton только отсутствующих секций, partial — по
|
|||||||
[аутентификации](../../domains/authentication/overview.md).
|
[аутентификации](../../domains/authentication/overview.md).
|
||||||
|
|
||||||
Согласованное поведение раскрыто в workspace-спецификациях
|
Согласованное поведение раскрыто в workspace-спецификациях
|
||||||
[UI resilience](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/frontend-ui-resilience/spec.md),
|
[UI resilience](../../../specs/openspec/specs/frontend-ui-resilience/spec.md),
|
||||||
[loading presentation](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/frontend-loading-presentation/spec.md)
|
[loading presentation](../../../specs/openspec/specs/frontend-loading-presentation/spec.md)
|
||||||
и [data resilience](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/frontend-data-resilience/spec.md).
|
и [data resilience](../../../specs/openspec/specs/frontend-data-resilience/spec.md).
|
||||||
|
|||||||
@@ -1,184 +0,0 @@
|
|||||||
# Контейнерная поставка
|
|
||||||
|
|
||||||
Поставка для одного Docker host, одного backend и краткого простоя.
|
|
||||||
[Топология](containers.md#контейнерная-поставка) и
|
|
||||||
[ADR 015](../adr/015-single-server-sqlite-deployment.md) объясняют границы.
|
|
||||||
Production-комплект находится в отдельном [репозитории deploy](https://git.personal.ksv741.ru/WealthLedger/deploy).
|
|
||||||
Его README — единственная пошаговая инструкция установки, обновления и recovery;
|
|
||||||
[ADR 016](../adr/016-versioned-deployment-bundle.md) объясняет выбор релиза комплекта.
|
|
||||||
Development-команды ниже выполняются из workspace с checkout `frontend/` и `backend/`.
|
|
||||||
|
|
||||||
## Предпосылки
|
|
||||||
|
|
||||||
- Docker Engine с Compose plugin **не ниже 2.24.4** (`!override` в development),
|
|
||||||
доступ к registry и достаточно свободного диска для non-root UID 1000.
|
|
||||||
Опубликованные images собраны для Linux amd64 (см. README комплекта).
|
|
||||||
Другую архитектуру проверяют отдельно; multi-platform images не обещаны.
|
|
||||||
- Nexus Docker hosted repository `docker-hosted`, доступный через
|
|
||||||
`nexus.personal.ksv741.ru/docker-hosted/`. Docker Bearer Token Realm включён;
|
|
||||||
reverse proxy передаёт исходные host и HTTPS scheme. Адрес token realm
|
|
||||||
в `WWW-Authenticate` также должен начинаться с `https://`.
|
|
||||||
Deployment policy запрещает redeploy существующих release tags.
|
|
||||||
Runner label — `ubuntu-latest`, runner должен иметь Docker CLI, buildx и daemon.
|
|
||||||
Наличие label само по себе не доказывает Docker capability.
|
|
||||||
- В Actions каждого component repo нужны `NEXUS_USERNAME` и
|
|
||||||
`NEXUS_PASSWORD` пользователя `wealthledger-ci`. Эта же учётная запись
|
|
||||||
используется сервером для загрузки образов. Для публикации нужны
|
|
||||||
browse/read/add/edit на `docker-hosted`; фактически выданный набор прав
|
|
||||||
настраивается в Nexus. Built-in
|
|
||||||
workflow token не заменяет эту настройку. Отдельной read-only учётной записи
|
|
||||||
сервера нет: `wealthledger-ci` работает и как CI writer, и как server reader,
|
|
||||||
поэтому её пароль на сервере даёт право публикации в `docker-hosted`. Это
|
|
||||||
принятое владельцем решение; разделение ролей — возможное усиление.
|
|
||||||
- На сервере Caddy уже завершает TLS и передаёт весь домен на
|
|
||||||
`127.0.0.1:8080` (или выбранный frontend port). Отдельного `/api` upstream нет.
|
|
||||||
DNS, TLS, firewall и provisioning Caddy выполняются вне этого runbook.
|
|
||||||
|
|
||||||
Серверный login и доступ к images проверяются на целевом сервере.
|
|
||||||
Локальный smoke не доказывает серверные registry permissions.
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
Из workspace с исходниками обоих компонентов:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker version
|
|
||||||
docker compose version
|
|
||||||
docker compose --env-file .env.development.example \
|
|
||||||
-f compose.yaml -f compose.development.yaml config --quiet
|
|
||||||
docker compose --env-file .env.development.example \
|
|
||||||
-f compose.yaml -f compose.development.yaml up -d --build --wait --wait-timeout 180
|
|
||||||
```
|
|
||||||
|
|
||||||
Откройте `http://localhost:5173`. Vite отдаёт HMR и проксирует `/api/` без
|
|
||||||
rewrite; Nest работает в watch mode. Монтируются только source/public,
|
|
||||||
`node_modules` и generated files остаются в container filesystem. `.env`
|
|
||||||
с хоста не копируется. После изменения manifest, lockfile, tsconfig или Vite
|
|
||||||
config пересоберите соответствующий image. `up --build` обновляет Linux
|
|
||||||
dependencies без stale named volume для `node_modules`.
|
|
||||||
|
|
||||||
Для другого порта скопируйте `.env.development.example` в игнорируемый
|
|
||||||
`.env.development` и согласованно поменяйте `FRONTEND_PORT`/`FRONTEND_ORIGIN`.
|
|
||||||
Пример содержит только вымышленные local secrets; server secrets сюда не входят.
|
|
||||||
Поля image/data directory нужны для интерполяции base Compose, но полностью
|
|
||||||
заменяются development override. В итоговом config нет production data path.
|
|
||||||
|
|
||||||
Project `wealthledger-dev` хранит SQLite в `development-data`. Обычные
|
|
||||||
`restart` и `down` сохраняют его. Не применяйте `down -v` к данным, которые
|
|
||||||
хотите сохранить. Не используйте production project name для development.
|
|
||||||
|
|
||||||
## Публикация компонентных images
|
|
||||||
|
|
||||||
Штатный release process каждого repo создаёт защищённый `vX.Y.Z` после
|
|
||||||
проверенного release commit. `image.yml` проверяет tag против package/lock
|
|
||||||
version, затем выполняет `npm ci`, `npm run verify`; backend дополнительно
|
|
||||||
выполняет `npm run test:e2e`. У каждого image OCI version/revision labels.
|
|
||||||
|
|
||||||
Images публикуются как `nexus.personal.ksv741.ru/docker-hosted/wealthledger/frontend:X.Y.Z`
|
|
||||||
и `.../backend:X.Y.Z`. `latest` не создаётся.
|
|
||||||
Workflow отказывается перезаписывать существующие release tags;
|
|
||||||
защита release tags и запрет параллельных publishers обязательны. Если одна
|
|
||||||
публикация уже состоялась, не удаляйте tag для повторного запуска: проверьте
|
|
||||||
опубликованный image и завершите проверку существующего artifact отдельно.
|
|
||||||
|
|
||||||
Выберите опубликованные version tags, выполните pull и сравните OCI revision
|
|
||||||
с release commit. Выполните smoke с disposable data/config до первого server
|
|
||||||
rollout. В `release.env` комплекта хранится пара полных image references, например
|
|
||||||
`nexus.personal.ksv741.ru/docker-hosted/wealthledger/frontend:1.1.0` и
|
|
||||||
`.../backend:2.1.0`. Сохраните versions/revisions
|
|
||||||
в deployment record. Release tags не должны переназначаться другим images.
|
|
||||||
|
|
||||||
## Перенос существующих images из Gitea
|
|
||||||
|
|
||||||
До переключения CI и server references проверьте HTTPS token realm и настройте
|
|
||||||
пользователя `wealthledger-ci`. В Actions добавьте `NEXUS_USERNAME`/`NEXUS_PASSWORD`; прежние
|
|
||||||
`REGISTRY_USERNAME`/`REGISTRY_TOKEN` сохраните для отката старого workflow.
|
|
||||||
Пароли вводятся интерактивно или через `--password-stdin`, не через аргументы.
|
|
||||||
|
|
||||||
Прежние images лежат в [Gitea packages](https://git.personal.ksv741.ru/WealthLedger/-/packages)
|
|
||||||
(`git.personal.ksv741.ru/wealthledger/{frontend,backend}`); новые публикуются
|
|
||||||
только в Nexus. Скопируйте каждый опубликованный release без пересборки. Например, для `0.2.0`
|
|
||||||
с установленным Skopeo и доступом чтения Gitea/записи Nexus:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
skopeo login git.personal.ksv741.ru
|
|
||||||
skopeo login nexus.personal.ksv741.ru
|
|
||||||
for component in frontend backend; do
|
|
||||||
skopeo copy --all --preserve-digests \
|
|
||||||
"docker://git.personal.ksv741.ru/wealthledger/$component:0.2.0" \
|
|
||||||
"docker://nexus.personal.ksv741.ru/docker-hosted/wealthledger/$component:0.2.0" || exit 1
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
`--all` переносит весь image index, включая платформы и вложенные attestation
|
|
||||||
manifests; `--preserve-digests` прерывает перенос при изменении digest.
|
|
||||||
До копирования проверьте наличие destination tag: если он уже существует,
|
|
||||||
сравните исходный и целевой index digest и вложенные manifests. Совпадающий
|
|
||||||
artifact повторно не публикуйте; несовпадение требует расследования, не удаления
|
|
||||||
или перезаписи tag. Частичный перенос не считается завершённым.
|
|
||||||
|
|
||||||
После копирования сравните digests через `skopeo inspect --raw` и SHA-256,
|
|
||||||
выполните `docker pull` обоих images пользователем `wealthledger-ci` на сервере приложения,
|
|
||||||
проверьте OCI revision и container smoke на disposable data.
|
|
||||||
Серверные шаги (login, pull, smoke) в репозитории не подтверждены и
|
|
||||||
выполняются оператором. Только после них меняйте
|
|
||||||
production image references и запускайте обычную
|
|
||||||
последовательность обновления ниже. Старые Gitea packages и references сохраните:
|
|
||||||
откат registry возвращает прежние адреса тех же artifacts, не восстанавливает
|
|
||||||
SQLite и не меняет encryption keys. Не переносите Git release tags и не
|
|
||||||
запускайте заново старый tag workflow для копирования существующего image.
|
|
||||||
|
|
||||||
## Первая установка
|
|
||||||
|
|
||||||
На подготовленном сервере оператор клонирует `deploy`, настраивает внешний
|
|
||||||
HTTPS и registry login, размещает server env, сертификат T-Bank и data directory
|
|
||||||
вне checkout. Для новой пустой базы команда вызывается с `--init`.
|
|
||||||
Для действующей установки сохраняются прежние данные и ключи, а первый переход
|
|
||||||
выполняется как полное обновление с backup. Отсутствующая БД без `--init` — ошибка.
|
|
||||||
Точные команды и разрешения каталогов приведены в README комплекта.
|
|
||||||
|
|
||||||
Git-тег `deploy` закрепляет пару images через `release.env`. Версия комплекта
|
|
||||||
не обязана совпадать с версиями компонентов. Публикация image не выпускает
|
|
||||||
комплект и не обновляет сервер автоматически. Команда получает выбранный тег,
|
|
||||||
загружает images до простоя и использует весь snapshot этого commit.
|
|
||||||
|
|
||||||
## Backup при остановленном backend
|
|
||||||
|
|
||||||
Сначала останавливаются писатели, затем копируется весь каталог данных,
|
|
||||||
включая возможные WAL/SHM. Только успешная копия допускает миграции. Локальные
|
|
||||||
backups находятся рядом с data directory, вне checkout, и автоматически
|
|
||||||
не удаляются. Незавершённая копия помечается `.partial-` и не подходит для restore.
|
|
||||||
Ключ шифрования хранится отдельно. Локальная копия не защищает от потери диска.
|
|
||||||
|
|
||||||
## Единый цикл обновления
|
|
||||||
|
|
||||||
Каждое обновление, включая повтор того же тега и изменение только frontend,
|
|
||||||
выполняет stop → backup → migration → readiness. Сравнение hashes контейнеров
|
|
||||||
и конфигурации не выполняется. Приняты короткий простой API и дополнительный
|
|
||||||
backup; во время простоя возможен 502, вкладкам может потребоваться reload.
|
|
||||||
Ошибка migration блокирует старт, ошибка готовности останавливает новый backend;
|
|
||||||
ошибка frontend не останавливает исправный backend. Успех фиксируется только
|
|
||||||
после проверок запуска обоих компонентов.
|
|
||||||
|
|
||||||
Одна file lock защищает установку от параллельных команд. Маркер незавершённого
|
|
||||||
обновления блокирует повтор до разбора оператором. Он содержит этап, версии
|
|
||||||
и путь к backup, но не секреты и не копию данных. После обновления нужен smoke
|
|
||||||
через HTTPS: healthcheck сам по себе не доказывает работу Secure refresh-cookie.
|
|
||||||
|
|
||||||
## Откат image
|
|
||||||
|
|
||||||
Откат версии и восстановление базы — разные операции. Прежний backend можно
|
|
||||||
запускать только после проверки совместимости с текущей схемой. Автоматического
|
|
||||||
rollback нет; migration service при ручном image rollback не запускается.
|
|
||||||
Порядок ручных Compose-команд и проверки recovery находится в README комплекта.
|
|
||||||
|
|
||||||
## Explicit restore
|
|
||||||
|
|
||||||
Оператор выбирает проверенную копию и явно принимает потерю всех более новых
|
|
||||||
записей. Сначала восстановление проверяется на isolated данных с прежним ключом
|
|
||||||
и совместимым image. На целевой установке останавливаются писатели, текущий
|
|
||||||
каталог сохраняется под новым именем, затем восстанавливается выбранная копия.
|
|
||||||
Backend пересоздаётся, чтобы заново подключить заменённый bind-каталог:
|
|
||||||
простой stop/start может сохранить mount прежних данных.
|
|
||||||
Проверяются целостность БД, auth/cards и расшифровка подключения. Только после
|
|
||||||
подтверждённого recovery можно снять маркер незавершённой операции.
|
|
||||||
+23
-73
@@ -2,76 +2,12 @@
|
|||||||
|
|
||||||
## Источник контракта
|
## Источник контракта
|
||||||
|
|
||||||
Backend генерирует OpenAPI из собственного публичного API. Этот сгенерированный
|
Backend генерирует OpenAPI из собственного публичного API. Frontend использует
|
||||||
OpenAPI — исполняемый источник HTTP-контракта. Frontend использует
|
производное описание контракта и типизированный клиент. Изменения проходят
|
||||||
предоставленный OpenAPI snapshot как актуальный вход и локально генерирует
|
[contract workflow](../development/runtime-openapi-verification.md).
|
||||||
типизированный клиент. Backend владеет контрактом, а подготовка и доставка
|
|
||||||
схемы находятся вне frontend. Это разделение принято в
|
|
||||||
[ADR 002](../adr/002-nest-generated-openapi-contract.md) и
|
|
||||||
[ADR 014](../adr/014-trusted-openapi-input.md).
|
|
||||||
|
|
||||||
Backend командой `npm run openapi:generate` создаёт `openapi.json` без запуска
|
|
||||||
сервера и не хранит результат в Git. Contract test сравнивает вывод генератора
|
|
||||||
с Nest runtime schema настроенного приложения. Frontend хранит полученный
|
|
||||||
artifact у себя и проверяет соответствие generated types этой копии. CI не
|
|
||||||
скачивает соседние репозитории и не сравнивает их текущие revisions. Обновление
|
|
||||||
входного artifact потребителя выполняется отдельно с review; совместимость
|
|
||||||
развёрнутых версий не доказывается одним зелёным локальным CI.
|
|
||||||
|
|
||||||
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
|
|
||||||
`{ data, meta }`. `data` nullable: `null` означает отсутствие complete result,
|
|
||||||
а подтверждённый пустой массив является available complete result. `meta`
|
|
||||||
независимо описывает freshness, completeness, `lastSuccessfulAt`,
|
|
||||||
resource-relevant `lastAttempt` и availability. `lastAttempt` имеет состояния
|
|
||||||
`none`, `running`, `succeeded`, `partial` и `failed`; его неуспех не заменяет
|
|
||||||
сохранённый complete result или его время. `Portfolio.isStale` остаётся
|
|
||||||
специфичным для snapshot полем, а не заменой состояния попытки.
|
|
||||||
|
|
||||||
Только Accounts дополнительно возвращает top-level `sync`: `null`, если у
|
|
||||||
текущего подключения ещё не было часовой попытки, иначе connection-wide
|
|
||||||
наблюдаемый результат. Это наблюдение, не команда. Обычный GET и retry после
|
|
||||||
проверки доступа читают локальное состояние и не начинают личную работу.
|
|
||||||
Устаревший `sync=observe`, пока он сохранён для совместимости маршрута, также
|
|
||||||
только читает. Request-started screen-sync command/status не является текущей
|
|
||||||
AS-IS моделью.
|
|
||||||
|
|
||||||
Безопасные ошибки завершённой часовой попытки передаются в `lastAttempt` и
|
|
||||||
Accounts `sync`; HTTP transport/access/input errors сохраняют Problem Details.
|
|
||||||
Старые данные при временной source failure сохраняют исходное успешное время.
|
|
||||||
Ни одно поле не раскрывает connection revision. Детали поведения —
|
|
||||||
[свежесть и полнота](cross-cutting/data-freshness.md).
|
|
||||||
|
|
||||||
Envelope несовместим с прежними bare-array ответами: backend/frontend
|
|
||||||
поставляются согласованной парой. Финальный artifact передаётся явно;
|
|
||||||
локальная генерация и CI frontend читают только собственный snapshot.
|
|
||||||
|
|
||||||
## Income analytics
|
|
||||||
|
|
||||||
`GET /api/v1/brokerage-accounts/{accountId}/income-analytics` и общий маршрут
|
|
||||||
`all` возвращают результат только выбранного периода: итоги по исходным
|
|
||||||
валютам, точные записи выплат, разбиение по инструментам и временные
|
|
||||||
интервалы. Данных сравнения с предыдущим периодом или годом ранее в ответе
|
|
||||||
нет — ни в теле, ни среди зависимостей `meta.dependencies`, роль которых
|
|
||||||
ограничена значением `selected`. Публичную машинную схему задаёт
|
|
||||||
backend-generated OpenAPI; frontend получает её обновлённый snapshot отдельно.
|
|
||||||
Точную семантику поведения описывает [доход](../domains/income/overview.md).
|
|
||||||
|
|
||||||
## Validation и identity
|
## Validation и identity
|
||||||
|
|
||||||
Backend преобразует и проверяет входные данные на общей HTTP-границе.
|
Backend преобразует и проверяет входные данные на общей HTTP-границе.
|
||||||
@@ -93,11 +29,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 отдельно
|
**Известное расхождение:** согласованная
|
||||||
фиксирует форму ответа.
|
[спецификация аутентификации](../../specs/openspec/specs/user-authentication/spec.md#requirement-стандартные-ответы-об-ошибках)
|
||||||
|
требует `pointer` и `detail` для невалидных полей. В текущей конфигурации
|
||||||
|
валидация формирует строковые сообщения, а фильтр ожидает структурированные
|
||||||
|
ошибки полей; поэтому корректность деталей `errors` не подтверждена.
|
||||||
|
Исправление требует отдельного изменения реализации и проверки полного
|
||||||
|
HTTP-потока. OpenAPI сам по себе этого расхождения не устраняет.
|
||||||
|
|
||||||
Ответ не включает автоматически все поля RFC (`type`, `title`, `instance`).
|
Ответ не включает автоматически все поля RFC (`type`, `title`, `instance`).
|
||||||
Добавление обязательного поля является изменением публичного контракта.
|
Добавление обязательного поля является изменением публичного контракта.
|
||||||
@@ -112,6 +53,15 @@ T-Bank token failure и временная недоступность имеют
|
|||||||
[Connection API](../integrations/tbank.md#состояние-и-ошибки). Raw upstream/Prisma errors
|
[Connection API](../integrations/tbank.md#состояние-и-ошибки). Raw upstream/Prisma errors
|
||||||
и secrets не передаются frontend.
|
и secrets не передаются frontend.
|
||||||
|
|
||||||
|
## Граница доказательства
|
||||||
|
|
||||||
|
Сравнение OpenAPI подтверждает согласованность схем, а не результат каждого
|
||||||
|
запроса. Безопасная обработка неожиданной ошибки проверяется отдельно.
|
||||||
|
Валидация, доступ, статусы и cookie требуют проверки полного HTTP-потока;
|
||||||
|
атомарность хранения — проверки с изолированной реальной базой.
|
||||||
|
Известное ограничение сквозных проверок описано в
|
||||||
|
[проверке backend](../development/verification.md).
|
||||||
|
|
||||||
## Наблюдаемость
|
## Наблюдаемость
|
||||||
|
|
||||||
Backend пишет структурированные JSON-события в stdout через Pino. Обработка
|
Backend пишет структурированные JSON-события в stdout через Pino. Обработка
|
||||||
@@ -130,5 +80,5 @@ client request ID. Это позволяет сопоставить публич
|
|||||||
Выбор logger и граница ответственности описаны в
|
Выбор logger и граница ответственности описаны в
|
||||||
[ADR 008](../adr/008-pino-backend-logging.md); согласованное поведение — в
|
[ADR 008](../adr/008-pino-backend-logging.md); согласованное поведение — в
|
||||||
workspace-спецификации
|
workspace-спецификации
|
||||||
[Structured logging](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/backend-structured-logging/spec.md).
|
[Structured logging](../../specs/openspec/specs/backend-structured-logging/spec.md).
|
||||||
Сборщик, срок хранения журналов и alerting не определяются приложением.
|
Сборщик, срок хранения журналов и alerting не определяются приложением.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
extends: ["@commitlint/config-conventional"],
|
|
||||||
defaultIgnores: false,
|
|
||||||
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"],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
# Визуальные принципы WealthLedger
|
|
||||||
|
|
||||||
Интерфейс использует общую визуальную основу на Mantine: цветовые роли,
|
|
||||||
типографику, интервалы, радиусы, тени и адаптивные размеры.
|
|
||||||
Поддерживается светлая схема; тёмная тема не заявлена.
|
|
||||||
|
|
||||||
## Общая основа и каталог
|
|
||||||
|
|
||||||
Внутри frontend используются три private npm workspace-пакета:
|
|
||||||
|
|
||||||
| Пакет | Ответственность |
|
|
||||||
| --- | --- |
|
|
||||||
| `@wealthledger/tokens` | JSON со значениями палитры, типографики, интервалов и других визуальных параметров |
|
|
||||||
| `@wealthledger/ui` | Mantine-тема, общие стили, StateView, LoadingSkeleton и EntryCard |
|
|
||||||
| `@wealthledger/icons` | React-иконки добавления, редактирования и удаления |
|
|
||||||
|
|
||||||
EntryCard отвечает за расположение переданных подписей, значения и действий.
|
|
||||||
Предметный компонент определяет смысл операции, форматирует данные и выбирает
|
|
||||||
цветовую роль. Денежные расчёты и запросы не входят в общий UI-пакет.
|
|
||||||
|
|
||||||
Иконки наследуют цвет через `currentColor`. По умолчанию они декоративные:
|
|
||||||
доступное имя принадлежит кнопке. Самостоятельная смысловая иконка получает
|
|
||||||
явные роль изображения и доступное имя. Предметные рисунки и анимации
|
|
||||||
остаются в приложении; поставки raw SVG и мобильного UI нет.
|
|
||||||
|
|
||||||
Storybook объединяет примеры приложения и пакетов. Он показывает синтетические
|
|
||||||
состояния без API, авторизации и query providers; формы получают локальные
|
|
||||||
callbacks. Каталог помогает проверять представление, но не заменяет проверки
|
|
||||||
запросов, денежных ограничений и восстановления после ошибок.
|
|
||||||
|
|
||||||
Пакеты используются локально и не опубликованы в registry. Независимый выпуск
|
|
||||||
не настроен. Потребители импортируют публичные exports пакетов напрямую; прежние
|
|
||||||
compatibility re-exports `shared/theme` и `shared/ui` удалены.
|
|
||||||
|
|
||||||
## Семантические роли
|
|
||||||
|
|
||||||
| Роль | Цветовая основа | Применение |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| canvas | gray.2 | Фон приложения |
|
|
||||||
| surface | white | Поверхности и формы |
|
|
||||||
| subtle | gray.0 | Приглушённая поверхность |
|
|
||||||
| text | dark.7 | Основной текст |
|
|
||||||
| muted | gray.6 | Вторичный текст |
|
|
||||||
| border | gray.3 | Граница поверхности |
|
|
||||||
| primary | blue filled | Основное действие и активная навигация |
|
|
||||||
| info | blue.6 | Информация |
|
|
||||||
| positive | green.9 | Положительный результат с текстом или знаком |
|
|
||||||
| negative | red.9 | Ошибка или отрицательный результат с текстом или знаком |
|
|
||||||
| warning | yellow.6 | Предупреждение; не мелкий текст на белом |
|
|
||||||
|
|
||||||
Цвет не заменяет подпись или знак. Палитра не гарантирует доступный контраст
|
|
||||||
произвольного сочетания текста и фона. Дополнительные grape/orange и отдельные
|
|
||||||
оформления карточек сохраняют предметный смысл соответствующего экрана;
|
|
||||||
они не образуют универсальную финансовую классификацию всей системы.
|
|
||||||
|
|
||||||
## Состояния и действия
|
|
||||||
|
|
||||||
Основное, вторичное и вспомогательное действия различаются по визуальному
|
|
||||||
приоритету. Выполнение действия и его недоступность — разные состояния.
|
|
||||||
Пустой результат и ошибка содержат заголовок, пояснение и действие, когда
|
|
||||||
оно предусмотрено сценарием. Ошибка объявляется вспомогательным технологиям,
|
|
||||||
а кнопка имеет доступное название.
|
|
||||||
|
|
||||||
Первая загрузка показывает структуру ожидаемого содержимого, без вымышленных
|
|
||||||
сумм; область обозначается как занятая, декоративные фигуры не получают фокус.
|
|
||||||
Фоновое обновление сохраняет доступные данные с локальным индикатором.
|
|
||||||
Подробные правила — в [состояниях интерфейса](../architecture/cross-cutting/ui-resilience.md).
|
|
||||||
|
|
||||||
Финансовые значения сохраняют валюту, знак и различие между нулём и
|
|
||||||
неизвестной суммой. Предметные представления раскрыты в
|
|
||||||
[портфеле](../domains/portfolio/overview.md), [операциях](../domains/operations/overview.md)
|
|
||||||
и [банковских картах](../domains/cards/overview.md).
|
|
||||||
|
|
||||||
Согласованная общая основа описана в
|
|
||||||
[спецификации дизайн-системы](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/frontend-design-system/spec.md).
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Сопровождение системы
|
||||||
|
|
||||||
|
Здесь описаны локальное окружение, согласованность компонентов, границы
|
||||||
|
проверки и поддержание проектных знаний. Команды разработки и coding conventions
|
||||||
|
принадлежат [frontend](../../frontend/AGENTS.md) и [backend](../../backend/AGENTS.md).
|
||||||
|
|
||||||
|
- [Локальное окружение](local-workspace.md) — предпосылки работы, первый пользовательский сценарий и диагностика.
|
||||||
|
- [Проверки системы](verification.md) — что доказывают проверки интерфейса, API и хранения данных, известные ограничения.
|
||||||
|
- [HTTP contract](runtime-openapi-verification.md) — направление генерации OpenAPI и проверка совместимости.
|
||||||
|
- [Дизайн-система](design-system.md) / [UI-каталог](ui-catalog.md) — визуальные роли, реальные состояния и ограничения примеров.
|
||||||
|
- [Принципы изменений](engineering-governance.md) — связность системы, риск и доказательства результата.
|
||||||
|
- [Ведение документации](../CONTRIBUTING.md) — самостоятельные объяснения и их обновление вместе с системой.
|
||||||
|
- [Контекст знаний](agent-token-efficiency.md) / [проверочные вопросы](agent-context-evals.md) — поиск ответа и обнаружение пробелов документации.
|
||||||
|
- [OpenSpec](openspec-authority-and-lifecycle.md) — роль сохранённых требований и статус прежнего процесса.
|
||||||
|
|
||||||
|
## Исторические свидетельства
|
||||||
|
|
||||||
|
[Remediation registry](audit-remediation.md) и [Completion Evidence](completion-evidence.md)
|
||||||
|
сохраняют статусы, исходные адреса и результаты прежних проверок.
|
||||||
|
Эти материалы не входят в самостоятельное описание текущей системы; их внутренние ссылки
|
||||||
|
могут требовать архивного окружения и не являются условием чтения документации.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# Проверка полноты проектных знаний
|
||||||
|
|
||||||
|
Эти вопросы помогают оценить, объясняет ли документация систему без доступа
|
||||||
|
к исходникам. Для каждого вопроса важны правильный ответ, существенное
|
||||||
|
ограничение и отсутствие ошибочного вывода из похожего сценария.
|
||||||
|
|
||||||
|
## Cases
|
||||||
|
|
||||||
|
| Вопрос | Объяснение | Существенная граница |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Кто подтверждает доступ к брокерскому счёту? | [Обзор](../overview.md#как-связаны-представления) | Сессия подтверждает пользователя, но идентификатор счёта не доказывает доступ |
|
||||||
|
| Как меняются финансовые данные при замене token? | [Подключение](../integrations/tbank.md#граница-финансового-кэша) | Данные прежнего поколения не становятся сохранённым снимком нового подключения |
|
||||||
|
| Что происходит при конкурентном refresh? | [Authentication](../domains/authentication/overview.md), [ADR 009](../adr/009-concurrent-refresh-rotation-policy.md) | Один победитель; заменённый token не отзывает его сессию сам по себе |
|
||||||
|
| Как восстанавливается интерфейс после ошибки обновления? | [UI resilience](../architecture/cross-cutting/ui-resilience.md) | Фоновая ошибка отличается от первой загрузки и смены пользователя |
|
||||||
|
| Чем будущая выплата отличается от дохода? | [События](../domains/income/upcoming-events.md), [доходы](../domains/income/overview.md) | Прогноз не подтверждает личное зачисление |
|
||||||
|
| Можно ли складывать разные валюты или заменить неизвестное нулём? | [Money](../glossary.md#money) | Точность, валюта и отсутствие значения сохраняются отдельно |
|
||||||
|
| Откуда рейтинг и доходность облигации? | [Bond detail](../domains/instruments/bonds.md) | Рейтинг эмитента не заменяет рейтинг выпуска; рыночная доходность не равна личному результату |
|
||||||
|
| Доказывает ли OpenAPI правильность ошибки запроса? | [HTTP API](../architecture/http-api.md#error-contract) | Равенство схем не доказывает полный HTTP-поток |
|
||||||
|
| Что гарантирует проверка хранения данных? | [Backend](verification.md#api-и-хранение-данных) | Проверка с подставными ответами не доказывает атомарность реальной базы |
|
||||||
|
| Как ограничиваются обращения к внешнему сервису? | [T-Bank](../integrations/tbank.md#конфигурация-и-ограничения) | Повторы, очередь и лимит ограничены; TLS остаётся включённым |
|
||||||
|
| Защищает ли фильтрация логов любое сообщение? | [ADR 008](../adr/008-pino-backend-logging.md) | Секрет в свободном тексте требует отдельной проверки |
|
||||||
|
| Есть ли восстановление пароля по письму? | [Ограничения auth](../domains/authentication/overview.md#ограничения) | Пример будущего сценария не является поставленной возможностью |
|
||||||
|
|
||||||
|
## Что измерять
|
||||||
|
|
||||||
|
Полезное объяснение позволяет найти ответ, назвать ограничение и связать его
|
||||||
|
с другими сценариями. Оцениваются неверные выводы о доступе, деньгах, полноте
|
||||||
|
данных и источниках, а также число уточнений до правильного понимания.
|
||||||
|
Время поиска и объём чтения имеют смысл только вместе с точностью ответа.
|
||||||
|
|
||||||
|
## Изменение правил
|
||||||
|
|
||||||
|
Если знание есть, но его трудно найти, уточняется навигация. Если документ
|
||||||
|
найден, но не объясняет различие понятий или ограничение, уточняется объяснение.
|
||||||
|
При конфликте источников сохраняется отметка о ручном решении; догадка не
|
||||||
|
подменяет установленный факт.
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Контекст знаний о системе
|
||||||
|
|
||||||
|
Цель навигации — дать читателю знания, необходимые для конкретного вопроса,
|
||||||
|
без предварительного чтения всей документации. Самостоятельное объяснение
|
||||||
|
связывает понятия, сценарии и ограничения, а не воспроизводит устройство кода.
|
||||||
|
|
||||||
|
## Routing context
|
||||||
|
|
||||||
|
[Тематическая карта](../README.md#предметные-области) направляет к нужному предмету:
|
||||||
|
аутентификация связана с безопасностью и сессиями, денежный показатель —
|
||||||
|
с его происхождением и валютой, ошибка интерфейса — с восстановлением данных.
|
||||||
|
Вопрос о внешнем виде не требует автоматически изучать хранение данных.
|
||||||
|
При обнаружении новой существенной границы расширяется и нужный контекст.
|
||||||
|
|
||||||
|
## Источники и конфликты
|
||||||
|
|
||||||
|
Публичный HTTP-контракт описывается OpenAPI backend; документация объясняет
|
||||||
|
систему, ADR сохраняют причины решений, исследования предлагают варианты.
|
||||||
|
Существующее поведение не отменяет согласованное требование, а рекомендация
|
||||||
|
из исследования не становится принятым решением автоматически.
|
||||||
|
|
||||||
|
При расхождении возможны устаревшее объяснение, дефект реализации или
|
||||||
|
незавершённое изменение. Неустановленный смысл, особенно в безопасности,
|
||||||
|
данных и публичном контракте, явно отмечается для ручного решения.
|
||||||
|
Карта ответственности за знания — [ведение документации](../CONTRIBUTING.md).
|
||||||
|
|
||||||
|
## Recovery и handoff
|
||||||
|
|
||||||
|
Продолжение работы опирается на ожидаемый результат, выполненные изменения,
|
||||||
|
оставшиеся вопросы, результаты проверок и ограничения. Дублирование требований
|
||||||
|
в нескольких независимых пересказах создаёт риск расхождения. Исторические
|
||||||
|
проверки сохраняют своё значение только для того состояния системы, в котором
|
||||||
|
они выполнялись; секреты не являются допустимым содержимым свидетельств.
|
||||||
|
|
||||||
|
## Проверка эффективности
|
||||||
|
|
||||||
|
[Проверочные вопросы](agent-context-evals.md) показывают, можно ли получить
|
||||||
|
правильную модель системы и отличить близкие понятия. Качество оценивается по
|
||||||
|
верности ответа, найденным ограничениям и необходимости дополнительных уточнений.
|
||||||
|
Число документов, их краткость или успешная проверка Markdown сами по себе
|
||||||
|
не доказывают полезность знания. Сравнение результатов требует одинаковых
|
||||||
|
вопросов и сопоставимых условий.
|
||||||
@@ -0,0 +1,579 @@
|
|||||||
|
# Audit remediation registry
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
|
||||||
|
Это единый versioned реестр текущего состояния remediation для findings из всех [технических audit snapshots](../audits/README.md). Каждый аудит — неизменяемый source of facts; этот документ хранит только текущий статус, путь продолжения и evidence последующих решений, не копируя полный текст finding.
|
||||||
|
|
||||||
|
## Правила работы
|
||||||
|
|
||||||
|
- Каждая audit-derived запись имеет один статус: `planned`, `active`, `done`, `deferred` или `accepted`.
|
||||||
|
- Каждое уникальное external claim дополнительно имеет независимый triage
|
||||||
|
disposition: `confirmed`, `already-covered`, `rejected` или `deferred`.
|
||||||
|
Повтор одного underlying claim в нескольких reports получает один Audit ID со
|
||||||
|
ссылкой на каждый source. Disposition не является remediation status:
|
||||||
|
`confirmed` ещё может быть active, а `rejected` — accepted как проверенное
|
||||||
|
решение ничего не внедрять.
|
||||||
|
- Одновременно допускается не более одного `active` remediation change. Пока он существует, продолжается только он. При наличии `planned` ровно одна запись отмечается следующей рекомендуемой задачей, но она не запускается параллельно active change.
|
||||||
|
- Новая запись `done` требует даты и ссылки на фактически выполненную работу и её проверки (например, commit/PR или существующее evidence). OpenSpec archive не является обязательным; не создавайте отдельные artifacts только ради статуса.
|
||||||
|
- `deferred` и `accepted` требуют даты, rationale и review trigger либо ссылки на явное решение владельца.
|
||||||
|
- При смене статуса обновляется только этот реестр; versioned audit snapshot не редактируется.
|
||||||
|
|
||||||
|
Текущая проверка результата определена в
|
||||||
|
[engineering governance](engineering-governance.md#проверка-результата).
|
||||||
|
Исторические записи
|
||||||
|
ниже, прежние ссылки на `completion-evidence.md` в docs и датированные audit
|
||||||
|
snapshots сохраняются без переноса или переписывания задним числом.
|
||||||
|
|
||||||
|
Датированные dispositions ниже описывают проверки и решения своего времени;
|
||||||
|
они не восстанавливают прежние SDD prerequisites. Live ссылки ведут к текущим
|
||||||
|
owners, а историческое evidence сохраняется по исходным адресам.
|
||||||
|
|
||||||
|
## Текущее состояние workflow
|
||||||
|
|
||||||
|
- Bootstrap change: [`2026-08-23-establish-audit-remediation-workflow`](../../specs/openspec/changes/archive/2026-08-23-establish-audit-remediation-workflow/) — `archived`; это process foundation и не закрывает ни одного Audit ID.
|
||||||
|
- Последний завершённый audit remediation change: [`2026-09-06-complete-frontend-audit-remediation`](../../specs/openspec/changes/archive/2026-09-06-complete-frontend-audit-remediation/) — `archived`; FE26-02–FE26-18 закрыты, FE26-01 сохраняет прежнее evidence.
|
||||||
|
- Единственный active change: отсутствует.
|
||||||
|
|
||||||
|
## Текущая задача
|
||||||
|
|
||||||
|
Frontend audit remediation завершена; [Completion Evidence][FE26-E] содержит
|
||||||
|
archive, delivery commits, verification и независимый verdict. Следующий
|
||||||
|
remediation change создаётся после выбора владельцем продолжения.
|
||||||
|
|
||||||
|
## Следующая рекомендуемая задача
|
||||||
|
|
||||||
|
`assess-auth-platform-options` для AUD24-02. Change применит Solution Assessment
|
||||||
|
к существующей реализации и зрелым auth platforms, включая Better Auth, и не
|
||||||
|
предрешает миграцию.
|
||||||
|
|
||||||
|
## Реестр findings
|
||||||
|
|
||||||
|
### Frontend audits: август 2026
|
||||||
|
|
||||||
|
Источник: [три неизменяемых snapshots](../audits/2026-08-frontend-repository-audits/README.md).
|
||||||
|
`G` — общий audit от 31 августа, `A28` — audit от 28 августа,
|
||||||
|
`S` — structural audit от 31 августа. Все 22 source findings сопоставлены
|
||||||
|
18 underlying claims ниже; одинаковые F IDs без source prefix неоднозначны.
|
||||||
|
|
||||||
|
Дата сопоставления и закрытия FE26-02–FE26-18: 06.09.2026.
|
||||||
|
[Completion Evidence][FE26-E] содержит общий archive path, delivery commits
|
||||||
|
всех четырёх областей, точные commands/results и независимый PASS.
|
||||||
|
`already-covered` обозначает прежнюю реализацию, подтверждённую текущей
|
||||||
|
проверкой, без фиктивной повторной implementation task.
|
||||||
|
Реализация прежних исправлений находится во frontend `c97f62b` и backend
|
||||||
|
`55b100d`; эти идентификаторы не доказывают исторические approvals или gates.
|
||||||
|
|
||||||
|
| Audit ID | Нормализованный claim | Sources | Disposition | Remediation status | Evidence / текущее состояние |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| FE26-01 | Общая граница финансового кэша при смене T-Bank connection. | [FE-A28] F1; [FE-S] F-01 | already-covered | done | 01.09.2026; [archive](../../specs/openspec/changes/archive/2026-09-01-harden-tbank-connection-cache-boundary/), delivery commits, gates и independent review — [существующее Completion Evidence](completion-evidence.md#2026-09-01-harden-tbank-connection-cache-boundary). |
|
||||||
|
| FE26-02 | Проверять соответствие generated types OpenAPI snapshot. | [FE-A28] F2; [FE-G] F-01 | already-covered | done | 06.09.2026; [FE26-E]. [check-openapi](../../frontend/scripts/check-openapi.mjs) включён в [verify](../../frontend/package.json); snapshot/types проверяются offline. Pre-commit состав честно описан в conventions. |
|
||||||
|
| FE26-03 | Events должен соблюдать принятую VM/view границу. | [FE-A28] F3 | confirmed | done | 06.09.2026; [FE26-E]. [Events VM](../../frontend/src/pages/account-events/ui/AccountEventsPage.vm.ts) владеет route/query/state/retry; [container](../../frontend/src/pages/account-events/ui/AccountEventsPage.tsx) использует прежние views. Остальные named pages уже разделены; см. FE26-13. |
|
||||||
|
| FE26-04 | Покрыть успешный public login journey. | [FE-G] F-02 | already-covered | done | 06.09.2026; [FE26-E]. [AppRoutes tests](../../frontend/src/app/routes/AppRoutes.test.tsx): форма, нормализованные credentials, реальный AuthProvider/MSW и protected landing. |
|
||||||
|
| FE26-05 | Безопасно отклонять невозможные календарные даты. | [FE-G] F-03 | already-covered | done | 06.09.2026; [FE26-E]. [Income VM](../../frontend/src/pages/account-income/ui/AccountIncomePage.vm.ts) проверяет finite date до ISO conversion; [regression tests](../../frontend/src/pages/account-income/ui/AccountIncomePage.test.tsx) покрывают invalid date и leap year. |
|
||||||
|
| FE26-06 | Включить type-only dependencies в public API guardrail. | [FE-G] F-04 | already-covered | done | 06.09.2026; [FE26-E]. [dependency-cruiser](../../frontend/dependency-cruiser.cjs) включает tsPreCompilationDeps; [shared API](../../frontend/src/shared/api/index.ts) экспортирует paths. |
|
||||||
|
| FE26-07 | Дать общим display/API primitives нейтральных владельцев. | [FE-G] F-05; [FE-S] F-07 | already-covered | done | 06.09.2026; [FE26-E]. [shared display](../../frontend/src/shared/lib/display.ts) владеет Money/date/logo; [apiErrorCode](../../frontend/src/shared/api/problem-details.ts) объединяет transport extraction. S F-07 расширяет тот же ownership claim API helper; повторной реализации нет. |
|
||||||
|
| FE26-08 | Синхронизировать entrypoint docs с фактическими gates. | [FE-G] F-06 | already-covered | done | 06.09.2026; [FE26-E]. [Frontend README](../../frontend/README.md), [conventions](../../frontend/.ai/conventions/frontend-ui.md) и [workflow](frontend-workflow.md) описывают существующие команды, не обещая удалённые runtime/hygiene checks. |
|
||||||
|
| FE26-09 | Убрать механическое обязательное дробление каждого component. | [FE-G] F-07 | already-covered | done | 06.09.2026; [FE26-E]. [UI pattern](../../frontend/.ai/conventions/frontend-ui.md) сохраняет semantic ownership и допускает tightly-coupled renderers; это guidance, а не line-count gate. Конкретное разделение крупных modules учтено отдельно FE26-13. |
|
||||||
|
| FE26-10 | Объединить protected query policy и endpoint query identity ownership. | [FE-G] F-08; [FE-S] F-05 | already-covered | done | 06.09.2026; [FE26-E]. [App defaults](../../frontend/src/app/ui/App.tsx), [T-Bank VM](../../frontend/src/features/tbank-connection/ui/TbankConnectionSection.vm.ts) и entity-owned options operations/portfolio; status использует общий authenticated query seam. |
|
||||||
|
| FE26-11 | Централизовать test cleanup и auth reset. | [FE-S] F-02 | already-covered | done | 06.09.2026; [FE26-E]. [Test setup](../../frontend/src/test/setup.ts): RTL cleanup, auth reset, MSW handler reset в общем afterEach. |
|
||||||
|
| FE26-12 | Убрать повтор server identity из Income view/model. | [FE-S] F-03 | confirmed | done | 06.09.2026; [FE26-E]. [Backend income service](../../backend/src/modules/investments/income/income-analytics.service.ts) передаёт instrumentKey; [Income VM](../../frontend/src/pages/account-income/ui/AccountIncomePage.vm.ts) проверяет и группирует готовую связь, не вычисляя identity. |
|
||||||
|
| FE26-13 | Выделить самостоятельные ответственности семи крупных TSX modules. | [FE-S] F-04 | already-covered | done | 06.09.2026; [FE26-E]. Income/Events/Accounts/Account state views, [route fallback](../../frontend/src/app/routes/RouteFallback.tsx), [guards](../../frontend/src/app/routes/RouteGuards.tsx), breadcrumbs и T-Bank loaded section выделены во frontend `c97f62b`. |
|
||||||
|
| FE26-14 | Устранить повтор test client policy и нестабильный cache lifetime. | [FE-S] F-06 | confirmed | done | 06.09.2026; [FE26-E]. [Test query factory](../../frontend/src/test/query.tsx) используется оставшимися auth/T-Bank harnesses; [auth test](../../frontend/src/features/auth/model/auth.test.tsx) проверяет сохранение client и seeded cache при rerender. |
|
||||||
|
| FE26-15 | Перенести компонентные стили из global CSS к владельцам. | [FE-S] F-08 | already-covered | done | 06.09.2026; [FE26-E]. [Global styles](../../frontend/src/shared/theme/styles.css) содержат базовые правила; AppShell, breadcrumbs и auth используют CSS Modules, StateView — Mantine props. |
|
||||||
|
| FE26-16 | Исправить source connectedAt contract вместо String coercion. | [FE-S] F-09 | already-covered | done | 06.09.2026; [FE26-E]. [Backend DTO](../../backend/src/modules/investments/tbank-connection/tbank-connection.dto.ts) задаёт nullable date-time string; [frontend consumer](../../frontend/src/features/tbank-connection/ui/TbankConnectionSectionLoaded.tsx) не скрывает неверный transport type через String. |
|
||||||
|
| FE26-17 | Перенести auth validation и payment invariants из JSX к model owners. | [FE-S] F-10 | already-covered | done | 06.09.2026; [FE26-E]. [Auth validation](../../frontend/src/features/auth/model/validation.ts), [ledger display](../../frontend/src/entities/broker-operation/model/display.ts) владеют соответствующими правилами. |
|
||||||
|
| FE26-18 | Удалить redundant position modes и test-only proxy exports. | [FE-S] F-11 | already-covered | done | 06.09.2026; [FE26-E]. [PositionCard](../../frontend/src/pages/account/ui/components/PositionCard.view.tsx) выводит mode из kind; [StateView](../../frontend/src/shared/ui/StateView.tsx) не экспортирует Mantine Button; page barrels сохраняют product surface. |
|
||||||
|
|
||||||
|
[FE26-E]: completion-evidence.md#2026-09-06-complete-frontend-audit-remediation
|
||||||
|
[FE-G]: ../audits/2026-08-frontend-repository-audits/frontend-repository-audit.md
|
||||||
|
[FE-A28]: ../audits/2026-08-frontend-repository-audits/frontend-repository-audit-2026-08-28.md
|
||||||
|
[FE-S]: ../audits/2026-08-frontend-repository-audits/frontend-structural-maintainability-audit.md
|
||||||
|
|
||||||
|
### Повторный multi-source review WealthLedger SDD v2 26.08.2026
|
||||||
|
|
||||||
|
Шесть исходных reports находятся в [immutable snapshot set](../audits/2026-08-26-wealth-ledger-sdd-v2-review/README.md).
|
||||||
|
Сокращения: [D2] — DeepSeek, [G2] — Gemini, [Gr2] — Grok, [K2] — Kimi,
|
||||||
|
[Q2] — Qwen, [Z2] — Z.AI. Оценки и предложенные patches не принимаются без
|
||||||
|
сопоставления с воспроизводимым project invariant.
|
||||||
|
|
||||||
|
| Audit ID | Нормализованный claim | Sources | Disposition | Remediation status | Evidence, rationale или review trigger |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| SDD26R2-01 | Сократить и визуально приоритизировать instructions, сохранив самодостаточные critical rules и единый основной язык. | [D2], [Gr2], [Z2] | confirmed | done | 2026-08-26; [archive](../../specs/openspec/changes/archive/2026-08-26-harden-openspec-sdd-agent-resilience/); specs `8462a3ba75d6dda491b9221699357c58d8f8e3cc`, `779a1e1305f8c209a4f98fae7f87718e48d61b37`; docs `d80e2e32b2ebadcd93a53105e1ac337fe2c1f4f6`; commands/results: [Completion Evidence](completion-evidence.md#2026-08-26-harden-openspec-sdd-agent-resilience). |
|
||||||
|
| SDD26R2-02 | Delta Spec template не должен предрешать `ADDED` и должен показывать все допустимые operations без активных пустых sections. | [K2] | confirmed | done | 2026-08-26; тот же [archive](../../specs/openspec/changes/archive/2026-08-26-harden-openspec-sdd-agent-resilience/), delivery commits и [Completion Evidence](completion-evidence.md#2026-08-26-harden-openspec-sdd-agent-resilience). |
|
||||||
|
| SDD26R2-03 | Planning verifier должен отличать defect артефакта от неверного invocation и failure окружения. | [Z2], [Q2] | confirmed | done | 2026-08-26; тот же [archive](../../specs/openspec/changes/archive/2026-08-26-harden-openspec-sdd-agent-resilience/), delivery commits и [Completion Evidence](completion-evidence.md#2026-08-26-harden-openspec-sdd-agent-resilience). Retry counter отклонён владельцем как лишнее состояние. |
|
||||||
|
| SDD26R2-04 | Apply должен читать точные native `contextFiles` и локальные правила affected repositories. | [K2] | confirmed | done | 2026-08-26; тот же [archive](../../specs/openspec/changes/archive/2026-08-26-harden-openspec-sdd-agent-resilience/), delivery commits и [Completion Evidence](completion-evidence.md#2026-08-26-harden-openspec-sdd-agent-resilience); отдельный context mapping не введён. |
|
||||||
|
| SDD26R2-05 | `N/A` rationale должно объяснять техническую необходимость, но его смысл нельзя надёжно валидировать простым text heuristic. | [Q2] | confirmed | done | 2026-08-26; тот же [archive](../../specs/openspec/changes/archive/2026-08-26-harden-openspec-sdd-agent-resilience/), delivery commits и [Completion Evidence](completion-evidence.md#2026-08-26-harden-openspec-sdd-agent-resilience); semantic sufficiency оставлена owner/reviewer. |
|
||||||
|
| SDD26R2-06 | Граница mechanical Apply fix должна явно исключать Requirement, Scenario, architecture и Verification changes. | [Q2], [Z2] | confirmed | done | 2026-08-26; тот же [archive](../../specs/openspec/changes/archive/2026-08-26-harden-openspec-sdd-agent-resilience/), delivery commits и [Completion Evidence](completion-evidence.md#2026-08-26-harden-openspec-sdd-agent-resilience). |
|
||||||
|
| SDD26R2-07 | Exact Requirement heading в `Covers` нужно копировать дословно и проверять детерминированно. | [Z2] | already-covered | accepted | 2026-08-26; SDD26-12 уже требует exact heading, а planning verifier отклоняет неизвестную reference и uncovered Requirement. Review trigger: удаление или ослабление exact-reference check. |
|
||||||
|
| SDD26R2-08 | Добавить `Target Repositories` в Proposal или расширить owner area произвольными repository paths. | [G2], [Gr2] | rejected | accepted | 2026-08-26; Proposal сохраняет intent без implementation topology; фиксированная Delivery Map отражает фактические четыре repos. Пересмотреть только при добавлении новой delivery area в workspace. |
|
||||||
|
| SDD26R2-09 | Хранить machine-readable approval markers внутри versioned artifacts. | [Gr2], [K2] | rejected | accepted | 2026-08-26; approval identity принадлежит разговору с Change Owner. Marker дублировал бы external state и мог бы ложно выглядеть действующим после upstream change. Review trigger: воспроизводимый случай ложного approval после потери conversation state. |
|
||||||
|
| SDD26R2-10 | Добавить отдельные verify/archive/DoD artifacts или phase. | [Gr2], [K2] | already-covered | accepted | 2026-08-26; SDD26-11 уже закрыт dispatcher, semantic verification, independent review, hard archive blockers и post-archive Completion Evidence без второго workflow artifact. Review trigger: доказанный terminal gap, который эти gates не выражают. |
|
||||||
|
| SDD26R2-11 | Ослабить последовательность Specs → Design для exploratory feasibility. | [Gr2] | rejected | accepted | 2026-08-26; production change сохраняет утверждённый behavior до architecture. Throwaway feasibility work допустима вне Apply, но не становится versioned parallel plan. Review trigger: повторяемый change, где безопасная feasibility work блокируется текущим graph. |
|
||||||
|
| SDD26R2-12 | Вынести Solution Assessment в отдельный artifact. | [Gr2] | rejected | accepted | 2026-08-26; conditional section остаётся в Design, где сравнение вариантов непосредственно обосновывает architecture; отдельный artifact усложнил бы graph без нового invariant. Review trigger: появление independently reusable assessment с отдельным approval owner. |
|
||||||
|
| SDD26R2-13 | Требовать отдельную workspace-task для semantic review каждого change. | [Gr2] | already-covered | accepted | 2026-08-26; terminal semantic verification и independent read-only review обязательны; workspace task создаётся в Tasks для orchestration, когда она нужна delivery plan. Review trigger: semantic review, пропущенный из-за отсутствия task-level representation. |
|
||||||
|
| SDD26R2-14 | Добавить Success Metrics в Proposal. | [Gr2] | rejected | accepted | 2026-08-26; observable acceptance принадлежит Requirements и Scenarios. Универсальные metrics в intent artifact создавали бы неподтверждённые цели без измеримого project need. Review trigger: утверждённая product metric, которую нельзя выразить Requirements/Scenarios. |
|
||||||
|
| SDD26R2-15 | Вести schema changelog рядом с version field. | [Gr2] | rejected | accepted | 2026-08-26; versioned Git history, OpenSpec change и archive уже фиксируют rationale и diff. Второй changelog не добавляет authority. Review trigger: наблюдаемая невозможность восстановить schema migration из canonical history. |
|
||||||
|
| SDD26R2-16 | Добавить CI/CD validation. | [D2] | deferred | deferred | 2026-08-26; текущий contract сознательно использует canonical local gates под Node 22. Review trigger: появление project CI. |
|
||||||
|
| SDD26R2-17 | Добавить dependencies между changes, sizing задач и commit convention. | [K2] | already-covered | accepted | 2026-08-26; dependencies остаются deferred в SDD26-15 до реального conflict; sizing отклонён в SDD26-16, commit convention — в SDD26-19. Review triggers наследуются из этих записей; повторный report не даёт нового evidence. |
|
||||||
|
| SDD26R2-18 | Exact schema version и criteria Solution Assessment отсутствуют или недостаточны. | [D2] | already-covered | accepted | 2026-08-26; `.cli-version`, schema version, controlled upgrade contract и explicit assessment trigger areas уже закреплены schema/governance. Review trigger: assessment, для которого текущие trigger areas не дают однозначного решения. |
|
||||||
|
|
||||||
|
[D2]: ../audits/2026-08-26-wealth-ledger-sdd-v2-review/deepseek-audit.md
|
||||||
|
[G2]: ../audits/2026-08-26-wealth-ledger-sdd-v2-review/gemini-audit.md
|
||||||
|
[Gr2]: ../audits/2026-08-26-wealth-ledger-sdd-v2-review/grok-audit.md
|
||||||
|
[K2]: ../audits/2026-08-26-wealth-ledger-sdd-v2-review/kimi-audit.md
|
||||||
|
[Q2]: ../audits/2026-08-26-wealth-ledger-sdd-v2-review/qwen-audit.md
|
||||||
|
[Z2]: ../audits/2026-08-26-wealth-ledger-sdd-v2-review/zai-audit.md
|
||||||
|
|
||||||
|
### Multi-source assessment WealthLedger SDD 26.08.2026
|
||||||
|
|
||||||
|
Raw external reports и checksums находятся в
|
||||||
|
[immutable snapshot set](../audits/2026-08-26-wealth-ledger-sdd/README.md).
|
||||||
|
Сокращения источников: [D] — DeepSeek, [G] — Gemini, [K] — Kimi, [Q] — Qwen,
|
||||||
|
[Z] — Z.AI. Ни одна внешняя рекомендация сама по себе не является project
|
||||||
|
authority.
|
||||||
|
|
||||||
|
| Audit ID | Нормализованный claim | Sources | Disposition | Remediation status | Evidence, rationale или review trigger |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| SDD26-01 | Снизить instruction bloat и устранить конкурирующих владельцев общих правил. | [D], [G], [Q], [Z] | confirmed | done | 2026-08-26; [`2026-08-26-harden-openspec-sdd-quality`](../../specs/openspec/changes/archive/2026-08-26-harden-openspec-sdd-quality/): schema v2 владеет semantics, templates — shape, config — только facts/language, scripts — hard invariants; отдельный `_rules.md` не вводится. |
|
||||||
|
| SDD26-02 | Проверять planning format машинно до approval и автоматически исправлять технические ошибки. | [D], [Z] | confirmed | done | 2026-08-26; stage-aware non-mutating verifier, controlled-invalid fixtures и format-only retry path поставлены в [`2026-08-26-harden-openspec-sdd-quality`](../../specs/openspec/changes/archive/2026-08-26-harden-openspec-sdd-quality/). |
|
||||||
|
| SDD26-03 | Каждая task должна иметь однозначную delivery area; multi-repo implementation нужно разделять. | [D], [G], [Z] | confirmed | done | 2026-08-26; schema v2 использует `[frontend\|backend\|specs\|docs\|workspace]`; planning verifier отклоняет owner из `N/A` area. |
|
||||||
|
| SDD26-04 | Добавить `Target Repositories` уже в Proposal. | [G] | rejected | accepted | 2026-08-26; Proposal остаётся owner-readable intent без implementation topology. Полный scope repositories определяется позже через обязательную Design Delivery Map. |
|
||||||
|
| SDD26-05 | Явно координировать порядок и совместимость multi-repository delivery. | [D], [G] | already-covered | accepted | 2026-08-26; Delivery Map, Contract Gate Decision, Migration Plan и workspace dispatcher уже задают owners, порядок, совместимость API и canonical gates. |
|
||||||
|
| SDD26-06 | Формализовать trigger для Solution Assessment. | [D] | already-covered | accepted | 2026-08-26; [engineering governance](engineering-governance.md#dependencies) и schema перечисляют auth, security, storage, integrations, observability, caching, concurrency и shared tooling. |
|
||||||
|
| SDD26-07 | Не допускать Design/Apply по неутверждённым artifacts. | [D], [K], [Z] | already-covered | accepted | 2026-08-26; human approval хранится в разговоре, one-artifact entrypoints останавливаются, downstream approvals каскадно инвалидируются. Фиктивный `approval.md` или status field не создаётся. |
|
||||||
|
| SDD26-08 | Разрешить partial/diff `MODIFIED` вместо полного requirement block. | [D] | rejected | accepted | 2026-08-26; native OpenSpec archive semantics требуют полного `MODIFIED`; собственный merge format создал бы второй transformation engine и риск потери scenarios. |
|
||||||
|
| SDD26-09 | Дать компактные примеры сложных delta operations. | [D], [K] | confirmed | done | 2026-08-26; schema v2 добавляет `MODIFIED`, `REMOVED` и `RENAMED` examples, сохраняя minimal ADDED-first template. |
|
||||||
|
| SDD26-10 | Генерировать в `spec.md` placeholders всех delta sections. | [K] | rejected | accepted | 2026-08-26; пустые sections повышают шум и риск неверной операции. Complex forms принадлежат instructions/examples, а template остаётся минимальным. |
|
||||||
|
| SDD26-11 | Явно определить DoD, verification и archive после Apply. | [D], [K], [Q] | already-covered | accepted | 2026-08-26; [engineering governance](engineering-governance.md#проверка-результата), workspace dispatcher, semantic verification, independent review и hard archive blockers уже образуют terminal protocol. Completion Evidence создаётся после факта, не в Tasks. |
|
||||||
|
| SDD26-12 | Связать каждую task с точными Delta Requirements и отклонять uncovered requirements. | [D], [K] | confirmed | done | 2026-08-26; schema v2 использует native identity `capability path — exact Requirement heading`; planning verifier проверяет ссылки и полное покрытие без собственного ID namespace. |
|
||||||
|
| SDD26-13 | Добавить Fast Track или сокращённую вторую schema для мелких fixes. | [G] | rejected | accepted | 2026-08-26; один lifecycle сохраняет предсказуемые approvals и archive blockers. Пропорциональность обеспечивают `skip_specs`, conditional assessment/rollback и узкие tasks. |
|
||||||
|
| SDD26-14 | Ограничивать agent context через tool-specific `.aiignore` и список repos из Proposal. | [G] | rejected | accepted | 2026-08-26; это не portable OpenSpec invariant. Context ограничивается Delivery Map, task owner и targeted reads; dependencies/build outputs уже исключаются локальными tool/repository rules. Пересмотреть при наблюдаемом context exhaustion. |
|
||||||
|
| SDD26-15 | Ввести общий `blocked_by`/`blocks` graph между changes. | [K] | deferred | deferred | Review trigger: первый реальный случай двух одновременно необходимых changes с доказанной ordering dependency, которую нельзя выразить текущим single-active-remediation rule. |
|
||||||
|
| SDD26-16 | Добавить priority и sizing в Tasks. | [K] | rejected | accepted | 2026-08-26; Tasks уже dependency-ordered и ограничены одной рабочей сессией. Оценочные points не защищают observable invariant и создают неподтверждённую точность. |
|
||||||
|
| SDD26-17 | Для труднообратимых changes нужен проверяемый rollback contract. | [K] | confirmed | done | 2026-08-26; Design template и planning verifier требуют trigger/actions/data safety/verification для risk cases либо конкретный `N/A` reason. |
|
||||||
|
| SDD26-18 | Добавить отдельный multi-repo context mapping file. | [K] | already-covered | accepted | 2026-08-26; native `openspec instructions ... --json` уже возвращает `planningHome`, `contextFiles`, `artifactPaths` и config context. Второй context map дублировал бы CLI state. |
|
||||||
|
| SDD26-19 | Зафиксировать commit-message convention в schema. | [K] | rejected | accepted | 2026-08-26; commit style не меняет artifact correctness и принадлежит repository delivery policy только при появлении отдельного проверяемого требования. |
|
||||||
|
| SDD26-20 | Сделать enterprise sections условными, чтобы снизить overhead. | [Q] | already-covered | accepted | 2026-08-26; Solution Assessment уже conditional, rollback допускает содержательный `N/A`; Delivery Map и Contract Gate Decision остаются обязательными, потому что защищают multi-repo scope. |
|
||||||
|
| SDD26-21 | Добавить явный feedback loop из Design/Apply в ранний затронутый artifact. | [Q] | confirmed | done | 2026-08-26; schema v2 и agent entrypoints маршрутизируют intent→Proposal, behavior→Specs, architecture/rollback→Design, decomposition→Tasks и инвалидируют downstream approvals. |
|
||||||
|
| SDD26-22 | Создавать `blocker.md` и `revision-request.md` при каждом blocker/revision. | [Q] | rejected | accepted | 2026-08-26; новые workflow artifacts не поддерживаются native schema и быстро устаревают. Наблюдаемое основание сообщается Change Owner в разговоре; durable artifact добавится только при реальном повторяемом trigger. |
|
||||||
|
| SDD26-23 | Требовать regression test для любой изменённой логики и happy-path test для любой новой. | [Q] | already-covered | accepted | 2026-08-26; [risk-based testing](engineering-governance.md#risk-based-testing) требует tests для существенных рисков и regressions, но осознанно отклоняет универсальные малоценные tests ради каждого logic/file change. |
|
||||||
|
| SDD26-24 | Добавлять Completion Summary в конец Tasks. | [Q] | rejected | accepted | 2026-08-26; Tasks содержат только planned work. Observed results и commits появляются после выполнения в verification/archive evidence, иначе checklist смешивает план и факты. |
|
||||||
|
| SDD26-25 | Требовать точный текстовый `STOP_PROTOCOL` после каждого artifact. | [Z] | rejected | accepted | 2026-08-26; обязательная остановка и явный approval уже enforced entrypoints. Фиксация одной строки ответа хрупка и не усиливает identity или semantic approval. |
|
||||||
|
| SDD26-26 | Сократить explanation `planningHome.root` до короткого imperative. | [Z] | rejected | accepted | 2026-08-26; пояснение предотвращает ошибочное вычисление path при store-backed и local modes OpenSpec 1.10. После удаления duplicate config rules instruction source остаётся приемлемым; пересмотреть при измеренном context regression. |
|
||||||
|
| SDD26-27 | Добавить дополнительные contract gates вроде `internal-proto`. | [G] | deferred | deferred | Review trigger: появление второго реального межсервисного committed contract, для которого `runtime-openapi` и repository gates недостаточны. До этого generic gate будет speculative. |
|
||||||
|
| SDD26-28 | Root project-owned agent projection должна восстанавливаться из versioned authority. | local triage | confirmed | done | 2026-08-26; superseded by follow-up cleanup: root `.opencode` остаётся local non-authority entrypoint без versioned projection source; hard constraints остаются в schema, docs и authority verifier. |
|
||||||
|
| SDD26-29 | Exact OpenSpec version должна быть единым compatibility contract, а не вечным literal в scripts/fixtures. | local triage | confirmed | done | 2026-08-26; `.cli-version` остаётся единственным current contract; markers/verifiers выводят expected value из него, новая stable не включает auto-upgrade. |
|
||||||
|
| SDD26-30 | Raw multi-source evidence и triage должны быть versioned раздельно. | local triage | confirmed | done | 2026-08-26; пять reports сохранены byte-for-byte в [snapshot set](../audits/2026-08-26-wealth-ledger-sdd/README.md); live registry хранит dispositions и statuses отдельно. |
|
||||||
|
|
||||||
|
[D]: ../audits/2026-08-26-wealth-ledger-sdd/deepseek-result.md
|
||||||
|
[G]: ../audits/2026-08-26-wealth-ledger-sdd/gemini-result.md
|
||||||
|
[K]: ../audits/2026-08-26-wealth-ledger-sdd/kimi-result.md
|
||||||
|
[Q]: ../audits/2026-08-26-wealth-ledger-sdd/qwen-result.md
|
||||||
|
[Z]: ../audits/2026-08-26-wealth-ledger-sdd/zai-result.md
|
||||||
|
|
||||||
|
### Аудит 13.08.2026
|
||||||
|
|
||||||
|
| Audit ID | Текущий статус | Связь и следующее действие | Terminal evidence или trigger |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| A-01 | done | См. WL-P2-03. | CAS rotation и regression test, 2026-08-23. |
|
||||||
|
| A-02 | accepted | Ограниченная revocation model закреплена в ADR 006; access token живёт 15 минут. | Пересмотреть, если продукт потребует немедленный отзыв access token. |
|
||||||
|
| A-03 | deferred | Production transport исключён из local MVP scope. | Пересмотреть перед hosted deployment или reverse-proxy contract. |
|
||||||
|
| A-04 | done | `docs/features/tbank-connection.md` описывает поставленные Accounts, Portfolio, Ledger и Future Events. | Current-state docs проверены 2026-08-24. |
|
||||||
|
| A-05 | done | См. WL-P1-03 и WL-P1-04. | Canonical root и hard archive lifecycle, 2026-08-23. |
|
||||||
|
| A-06 | done | См. WL-P2-10. | Architecture/development onboarding и link review, 2026-08-24. |
|
||||||
|
| A-07 | done | См. WL-P2-02. | P2002 mapping и concurrent registration regression, 2026-08-23. |
|
||||||
|
| A-08 | planned | Объединить validation payload review с `tighten-backend-request-feedback`. | Готово к выбору после active change. |
|
||||||
|
| A-09 | planned | Operations cache закрыт WL-P2-01; отдельно оценить session retention/index. | Создать scoped change после решения приоритета. |
|
||||||
|
| A-10 | deferred | Текущая operational mitigation описана в T-Bank feature doc. | Пересмотреть при наблюдаемой потере metadata или новом large-portfolio SLA. |
|
||||||
|
| A-11 | done | Исправлено и подтверждено самим snapshot 13.08.2026. | Transport tests и единый Bearer owner. |
|
||||||
|
| A-12 | done | Исправлено в snapshot; дополнительно защищено WL-P3-03. | Dependency boundary fixture passed, 2026-08-24. |
|
||||||
|
| A-13 | deferred | Frontend behavior закрыт; backend e2e зависит от WL-P2-05. | Тот же stable Prisma trigger, что у WL-P2-05. |
|
||||||
|
| A-14 | done | [`2026-08-24-harden-agent-sdd-governance`](../../specs/openspec/changes/archive/2026-08-24-harden-agent-sdd-governance/). | 2026-08-24; canonical SDD governance, local guardrails and completion evidence protocol verified. |
|
||||||
|
| A-15 | done | [`2026-08-24-harden-agent-sdd-governance`](../../specs/openspec/changes/archive/2026-08-24-harden-agent-sdd-governance/). | 2026-08-24; current-state ADR/spec contradictions fixed and strict specs passed. |
|
||||||
|
| A-16 | done | [`2026-08-24-harden-agent-sdd-governance`](../../specs/openspec/changes/archive/2026-08-24-harden-agent-sdd-governance/). | 2026-08-24; layered AGENTS and onboarding verified. |
|
||||||
|
| A-17 | deferred | Совпадает с accessibility scope WL-P3-04. | Пересмотреть при включении accessibility/second locale в MVP scope. |
|
||||||
|
| A-18 | done | [`2026-08-24-harden-agent-sdd-governance`](../../specs/openspec/changes/archive/2026-08-24-harden-agent-sdd-governance/). | 2026-08-24; main spec H1 normalized and strict validation passed. |
|
||||||
|
|
||||||
|
### Аудит 24.08.2026
|
||||||
|
|
||||||
|
| Audit ID | Цель | Статус | Связанный change | Следующее действие / evidence |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| AUD24-01 | Ввести risk-based testing без тестов ради файла или coverage. | done | [`2026-08-24-harden-agent-sdd-governance`](../../specs/openspec/changes/archive/2026-08-24-harden-agent-sdd-governance/) | 2026-08-24; risk-based testing policy and gates recorded. |
|
||||||
|
| AUD24-02 | Оценивать mature solutions и ретроспективно проверить auth platform. | planned | `assess-auth-platform-options` | Готово к созданию change; миграция не предрешена. |
|
||||||
|
| AUD24-03 | Закрепить строгий artifact graph и роли OpenSpec. | done | [`2026-08-24-harden-agent-sdd-governance`](../../specs/openspec/changes/archive/2026-08-24-harden-agent-sdd-governance/) | 2026-08-24; one-artifact approvals, cascade reapproval and artifact roles verified. |
|
||||||
|
| AUD24-04 | Синхронизировать agent projection и hard archive blockers. | done | [`2026-08-24-harden-agent-sdd-governance`](../../specs/openspec/changes/archive/2026-08-24-harden-agent-sdd-governance/) | 2026-08-24; project entrypoints and authority verifier reject bypass cases. |
|
||||||
|
| AUD24-05 | Добавить canonical repo gates и workspace dispatcher. | done | [`2026-08-24-harden-agent-sdd-governance`](../../specs/openspec/changes/archive/2026-08-24-harden-agent-sdd-governance/) | 2026-08-24; workspace dispatcher ran all affected gates and runtime OpenAPI. |
|
||||||
|
| AUD24-06 | Ввести docs-as-code gate и authoritative process policy. | done | [`2026-08-24-harden-agent-sdd-governance`](../../specs/openspec/changes/archive/2026-08-24-harden-agent-sdd-governance/) | 2026-08-24; docs `npm run verify` passed and engineering governance is durable docs. |
|
||||||
|
| AUD24-07 | Сделать AGENTS компактными routing/enforcement layers. | done | [`2026-08-24-harden-agent-sdd-governance`](../../specs/openspec/changes/archive/2026-08-24-harden-agent-sdd-governance/) | 2026-08-24; root and repository AGENTS layers verified. |
|
||||||
|
| AUD24-08 | Охватить одним registry все audit snapshots и исправить stale status. | done | [`2026-08-24-harden-agent-sdd-governance`](../../specs/openspec/changes/archive/2026-08-24-harden-agent-sdd-governance/) | 2026-08-24; registry covers all 53 audit IDs with one snapshot source each. |
|
||||||
|
| AUD24-09 | Исправить current-state ADR/spec contradictions. | done | [`2026-08-24-harden-agent-sdd-governance`](../../specs/openspec/changes/archive/2026-08-24-harden-agent-sdd-governance/) | 2026-08-24; ADR/spec contradictions fixed without editing audit snapshots. |
|
||||||
|
|
||||||
|
### P1
|
||||||
|
|
||||||
|
| Audit ID | Цель | Репозитории | Статус | Связанный change | Следующее действие | Terminal evidence |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| WL-P1-01 | Один владелец React Query cache identity. | frontend | done | [`2026-08-23-stabilize-frontend-query-and-strict-contracts`](../../specs/openspec/changes/archive/2026-08-23-stabilize-frontend-query-and-strict-contracts/) | — | 2026-08-23; frontend `1250581`, specs `b8a3f96`; `npm run verify` — passed (16 files, 48 tests). |
|
||||||
|
| WL-P1-02 | Сделать strict TypeScript исполняемым gate и устранить 16 ошибок. | frontend | done | [`2026-08-23-stabilize-frontend-query-and-strict-contracts`](../../specs/openspec/changes/archive/2026-08-23-stabilize-frontend-query-and-strict-contracts/) | — | 2026-08-23; frontend `1250581`, specs `b8a3f96`; strict typecheck and full `npm run verify` passed. |
|
||||||
|
| WL-P1-03 | Установить единственный canonical OpenSpec root. | specs | done | [`2026-08-23-harden-openspec-authority-and-lifecycle`](../../specs/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/) | — | 2026-08-23; frontend `d870d53`, backend `fee4f8c`, specs `51344d4`, docs `0bc8d1c`; authority verifier и 15 strict specs passed. |
|
||||||
|
| WL-P1-04 | Сделать archive lifecycle доказательством завершённости. | specs, docs | done | [`2026-08-23-harden-openspec-authority-and-lifecycle`](../../specs/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/) | — | 2026-08-23; frontend `d870d53`, backend `fee4f8c`, specs `51344d4`, docs `0bc8d1c`; hard archive gate и independent review passed. |
|
||||||
|
| WL-P1-05 | Разделить non-mutating lint check и auto-fix backend. | backend | done | [`2026-08-23-make-local-verification-agent-safe`](../../specs/openspec/changes/archive/2026-08-23-make-local-verification-agent-safe/) | — | 2026-08-23; backend `71d57d6`, specs `72444db`, docs `293df9c`; `npm run lint`, app/test typecheck и 17 suites / 51 tests — passed. |
|
||||||
|
|
||||||
|
### P2
|
||||||
|
|
||||||
|
| Audit ID | Цель | Репозитории | Статус | Связанный change | Следующее действие | Terminal evidence |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| WL-P2-01 | Ограничить размер и eviction operations cache. | backend, specs, docs | done | [`2026-08-23-harden-auth-and-cache-concurrency`](../../specs/openspec/changes/archive/2026-08-23-harden-auth-and-cache-concurrency/) | — | 2026-08-23; backend `c77b3cf`, specs `6e20cdf`, docs `8568e7b`; Node 22 lint, app/test typecheck и 56 unit tests passed. |
|
||||||
|
| WL-P2-02 | Вернуть предсказуемый 409 при конкурентной регистрации. | backend, specs, docs | done | [`2026-08-23-harden-auth-and-cache-concurrency`](../../specs/openspec/changes/archive/2026-08-23-harden-auth-and-cache-concurrency/) | — | 2026-08-23; backend `c77b3cf`, specs `6e20cdf`, docs `8568e7b`; P2002 mapping and barrier regression test passed. |
|
||||||
|
| WL-P2-03 | Сделать refresh rotation атомарной. | backend, specs, docs | done | [`2026-08-23-harden-auth-and-cache-concurrency`](../../specs/openspec/changes/archive/2026-08-23-harden-auth-and-cache-concurrency/) | — | 2026-08-23; backend `c77b3cf`, specs `6e20cdf`, docs `8568e7b`; CAS one-winner regression test passed. |
|
||||||
|
| WL-P2-04 | Синхронизировать runtime, OpenAPI и auth success statuses. | backend, frontend | done | [`2026-08-24-align-api-contract-and-client-resilience`](../../specs/openspec/changes/archive/2026-08-24-align-api-contract-and-client-resilience/) | — | 2026-08-24; backend `9bd3400`, frontend `6d892da`, specs `800450d`, docs `b91280c`; metadata regression, frontend OpenAPI check и runtime/snapshot SHA совпали. |
|
||||||
|
| WL-P2-05 | Восстановить runnable backend e2e gate. | backend | done | — | — | 2026-09-13; унаследованный RUST_LOG изолирован в Prisma config, 3 последовательных и 2 параллельных полных e2e passed. [Результаты](#2026-09-13--wl-p2-05-воспроизводимый-sqlite-e2e). |
|
||||||
|
| WL-P2-06 | Добавить проходящий app/test TypeScript gate backend. | backend | done | [`2026-08-23-make-local-verification-agent-safe`](../../specs/openspec/changes/archive/2026-08-23-make-local-verification-agent-safe/) | — | 2026-08-23; backend `71d57d6`, specs `72444db`, docs `293df9c`; `npm run typecheck:app` и `npm run typecheck:test` — passed. [2026-09-13: включён strict по P1-10](#2026-09-13--wl-p2-06-strict-typescript). |
|
||||||
|
| WL-P2-07 | Сохранять cached data во время background refetch. | frontend | done | [`2026-08-24-align-api-contract-and-client-resilience`](../../specs/openspec/changes/archive/2026-08-24-align-api-contract-and-client-resilience/) | — | 2026-08-24; frontend `6d892da`, specs `800450d`, docs `b91280c`; observable Accounts/Portfolio refetch tests и `npm run verify` — passed (17 files, 55 tests). |
|
||||||
|
| WL-P2-08 | Нормализовать cold-start refresh failure. | frontend | done | [`2026-08-24-align-api-contract-and-client-resilience`](../../specs/openspec/changes/archive/2026-08-24-align-api-contract-and-client-resilience/) | — | 2026-08-24; frontend `6d892da`, specs `800450d`, docs `b91280c`; cold-start и protected-401 transport-failure tests passed. |
|
||||||
|
| WL-P2-09 | Проверять backend runtime OpenAPI против frontend snapshot. | backend, frontend | done | [`2026-08-23-make-local-verification-agent-safe`](../../specs/openspec/changes/archive/2026-08-23-make-local-verification-agent-safe/) | — | 2026-08-23; frontend `7a3048c`, backend `71d57d6`, specs `72444db`, docs `293df9c`; runtime и snapshot SHA-256 совпали, frontend `npm run openapi:check` — passed. |
|
||||||
|
| WL-P2-10 | Актуализировать onboarding и постоянную документацию. | docs, frontend, backend, specs | done | [`2026-08-24-refresh-project-documentation-and-agent-entrypoints`](../../specs/openspec/changes/archive/2026-08-24-refresh-project-documentation-and-agent-entrypoints/) | — | 2026-08-24; frontend `be80ef2`, backend `fec542a`, specs `9f68ae5`, docs `88c0cd3`; OpenSpec/authority, link-path review и independent review passed. |
|
||||||
|
| WL-P2-11 | Зафиксировать одинаковый local bootstrap frontend/backend. | frontend, backend, docs | done | [`2026-08-23-make-local-verification-agent-safe`](../../specs/openspec/changes/archive/2026-08-23-make-local-verification-agent-safe/) | — | 2026-08-23; frontend `7a3048c`, backend `71d57d6`, specs `72444db`, docs `293df9c`; Node 22 policy и `npm ci` guidance зафиксированы. |
|
||||||
|
| WL-P2-12 | Проверить применимость high dependency advisories. | frontend, backend, specs, docs | done | [`2026-08-24-address-dependency-advisories-and-repo-hygiene`](../../specs/openspec/changes/archive/2026-08-24-address-dependency-advisories-and-repo-hygiene/) | — | 2026-08-24; frontend `826936c`, backend `6052565`, specs `8897bd9`, docs `60a1c33`; React Router обновлён, Prisma tooling risk принят с review trigger. |
|
||||||
|
| WL-P2-13 | Дать одному agent workflow единый local verify interface. | frontend, backend, specs, docs | deferred | — | Вернуться после восстановления WL-P2-05 тем же Prisma trigger; затем выбрать минимальный общий interface. | 2026-08-23; полный общий verdict не может быть достоверно зелёным, пока обязательный backend e2e migration gate блокируется Prisma 7.9.1. |
|
||||||
|
|
||||||
|
### P3
|
||||||
|
|
||||||
|
| Audit ID | Цель | Репозитории | Статус | Связанный change | Следующее действие | Terminal evidence |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| WL-P3-01 | Сделать unknown request fields явной ошибкой или документированной policy. | backend | planned | `tighten-backend-request-feedback` (ещё не создан) | Выбрать validation policy. | — |
|
||||||
|
| WL-P3-02 | Устранить shared i18n import cycle. | frontend | done | [`2026-08-24-tighten-frontend-quality-guardrails`](../../specs/openspec/changes/archive/2026-08-24-tighten-frontend-quality-guardrails/) | — | 2026-08-24; frontend `1fc269a`, specs `2029519`, docs `fa5db07`; Node 22 `npm run verify` — passed. |
|
||||||
|
| WL-P3-03 | Восстановить FSD public API boundary. | frontend | done | [`2026-08-24-tighten-frontend-quality-guardrails`](../../specs/openspec/changes/archive/2026-08-24-tighten-frontend-quality-guardrails/) | — | 2026-08-24; frontend `1fc269a`, specs `2029519`, docs `fa5db07`; dependency fixture gate — passed. |
|
||||||
|
| WL-P3-04 | Исправить базовые language/loading accessibility semantics. | frontend | deferred | — | Пересмотреть при включении accessibility в MVP scope. | 2026-08-24; текущий local MVP scope не включает accessibility remediation. |
|
||||||
|
| WL-P3-05 | Ввести пропорциональный bundle regression budget. | frontend | done | [`2026-08-24-tighten-frontend-quality-guardrails`](../../specs/openspec/changes/archive/2026-08-24-tighten-frontend-quality-guardrails/) | — | 2026-08-24; frontend `1fc269a`, specs `2029519`, docs `fa5db07`; initial JS payload 509,165 B ≤ 512,000 B. |
|
||||||
|
| WL-P3-06 | Добавить coverage ratchet только как regression signal. | frontend | done | [`2026-08-24-tighten-frontend-quality-guardrails`](../../specs/openspec/changes/archive/2026-08-24-tighten-frontend-quality-guardrails/) | — | 2026-08-24; frontend `1fc269a`, specs `2029519`, docs `fa5db07`; coverage 82.57/76.08/77.35/83.48% — passed. |
|
||||||
|
| WL-P3-07 | Защитить рабочий `.env` от случайного commit. | frontend, specs, docs | done | [`2026-08-24-address-dependency-advisories-and-repo-hygiene`](../../specs/openspec/changes/archive/2026-08-24-address-dependency-advisories-and-repo-hygiene/) | — | 2026-08-24; frontend `826936c`, specs `8897bd9`, docs `60a1c33`; hygiene check passed. |
|
||||||
|
| WL-P3-08 | Исключить runtime SQLite DB из Git. | backend, specs, docs | done | [`2026-08-24-address-dependency-advisories-and-repo-hygiene`](../../specs/openspec/changes/archive/2026-08-24-address-dependency-advisories-and-repo-hygiene/) | — | 2026-08-24; backend `6052565`, specs `8897bd9`, docs `60a1c33`; hygiene check passed. |
|
||||||
|
|
||||||
|
### 2026-09-13 — WL-P2-11: переход на Node 24 LTS
|
||||||
|
|
||||||
|
Продолжение bootstrap policy из WL-P2-11, части 14.backend/frontend/docs
|
||||||
|
[рабочего TODO](../../todo/wealthledger-engineering-improvements.md).
|
||||||
|
Node 24.21.0 / npm 11.19.0 выбран по [официальному каталогу релизов](https://nodejs.org/dist/index.json)
|
||||||
|
и [расписанию поддержки](https://github.com/nodejs/Release#release-schedule):
|
||||||
|
ветка Krypton LTS поддерживается до 30.04.2028.
|
||||||
|
|
||||||
|
`.nvmrc` frontend/backend/docs фиксируют 24.21.0, engines — 24.x.
|
||||||
|
Backend `@types/node` обновлён до 24.13.3; runtime dependencies не обновлялись.
|
||||||
|
Npm дополнил backend lockfile платформенными optional dependencies существующего
|
||||||
|
resolver. Для npm 11.19 явно разрешены install scripts сборочных зависимостей
|
||||||
|
с pin версий. Инструкция переустановки и отката — в
|
||||||
|
[локальном окружении](local-workspace.md#версия-node).
|
||||||
|
Specs продолжает наследовать frontend runtime; самостоятельный bootstrap CLI
|
||||||
|
из части 14.specs и CI остаются отдельными незавершёнными задачами.
|
||||||
|
|
||||||
|
Проверки относятся к frontend `53c852b`, backend `ce24a84`, docs `c74c573`,
|
||||||
|
specs `834f2d1` с текущим diff перехода Node:
|
||||||
|
|
||||||
|
- `npm ci` в frontend/backend/docs — PASS; backend native modules установлены
|
||||||
|
под Node 24, `npm run build` с генерацией Prisma client — PASS.
|
||||||
|
- Frontend `npm run verify` — PASS: 46 files, 268 tests, coverage, OpenAPI drift,
|
||||||
|
TypeScript, Biome, Knip, dependency boundaries, приложение и Storybook.
|
||||||
|
Прогон одновременно с установкой backend упирался в UI timeout; отдельный
|
||||||
|
повтор полного gate прошёл без изменения тестов или их таймаутов.
|
||||||
|
- Backend `npm run verify` — PASS: 35 suites, 257 tests, lint, оба typecheck и build.
|
||||||
|
- Backend `npm run test:e2e` — PASS: 8 suites, 98 tests; новая SQLite и штатные
|
||||||
|
migrations для каждой suite, включая runtime OpenAPI. Для HTTP-портов нужен
|
||||||
|
запуск вне sandbox. Рабочая БД не использовалась.
|
||||||
|
- Docs `npm run verify` — PASS: 54 Markdown-файла, 0 issues;
|
||||||
|
`git diff --check` во всех четырёх repos — PASS.
|
||||||
|
- OpenSpec CLI 1.10.0, существующая установка, исполненная Node 24.21.0:
|
||||||
|
`schema validate wealth-ledger-sdd --verbose` и
|
||||||
|
`validate --specs --strict --store wealth-ledger-specs` — PASS, 25 specs.
|
||||||
|
|
||||||
|
## История registry
|
||||||
|
|
||||||
|
Terminal evidence добавляется сюда только после завершения соответствующего change.
|
||||||
|
|
||||||
|
### 2026-09-13 — WL-P2-05: воспроизводимый SQLite e2e
|
||||||
|
|
||||||
|
Закрыты TODO 03 и 03.docs, повторно проверен SQLite scope 11. Backend
|
||||||
|
`8637ad2` с текущим diff, Node 22.21.0 / Prisma 7.9.1. Schema, migrations,
|
||||||
|
зависимости и HTTP-контракт не менялись.
|
||||||
|
|
||||||
|
- До исправления fresh migration: `RUST_LOG=warn` и `error` — exit 1 с пустым
|
||||||
|
`Schema engine error`; переменная отсутствует или равна `info` — exit 0.
|
||||||
|
В исходном окружении было `RUST_LOG=warn`. Это воспроизведённая зависимость
|
||||||
|
запуска от окружения, а не доказательство внутреннего дефекта Rust engine.
|
||||||
|
- Prisma config исключает унаследованный `RUST_LOG` после dotenv. Общий runner
|
||||||
|
даёт каждому suite свою DB и один URL для migration/Jest, отклоняет прямой
|
||||||
|
Jest без изоляции и очищает каталог после ошибки или сигнала.
|
||||||
|
- `npm run verify` — PASS: zero-warning lint, app/test types, 35 suites /
|
||||||
|
257 unit tests, Prisma generation и build.
|
||||||
|
- Docs `npm run verify` — PASS: Markdown, ссылки/якоря и `git diff --check`.
|
||||||
|
- `npm run test:e2e` — PASS: 8 suites / 98 tests, включая runtime OpenAPI.
|
||||||
|
- `node test/sqlite-regression.cjs` — PASS: три последовательных и два
|
||||||
|
параллельных полных e2e (каждый 98 tests, всего 40 разных DB); внешний
|
||||||
|
вымышленный файл неизменён. Недоступный schema engine возвращает exit 1;
|
||||||
|
SIGINT/SIGTERM — 130/143. Все 43 созданных DB-каталога удалены.
|
||||||
|
- `npm run test:durability -- /tmp/p111-baseline` — PASS: отдельная baseline
|
||||||
|
сборка `8ec172f48eefa2237dc0c16919fbb2e726b39a65`, additive migration,
|
||||||
|
сохранение User/Session/TbankConnection, банковской истории и защиты повторов,
|
||||||
|
restart, откат приложения и backup/restore. `RUST_LOG=info` удалён из runner;
|
||||||
|
для старого baseline унаследованная переменная также исключается.
|
||||||
|
- HTTP/durability проверены вне sandbox после воспроизведения запрета локальных
|
||||||
|
портов `EPERM`. Это ограничение среды, не failing migration. Пользовательская
|
||||||
|
DB и `.env` не использовались как цель записи.
|
||||||
|
|
||||||
|
Логи этой проверки: `/tmp/wl-sqlite-verify.log`, `/tmp/wl-sqlite-e2e-1.log`,
|
||||||
|
`/tmp/wl-sqlite-regression.log`, `/tmp/wl-sqlite-durability.log`.
|
||||||
|
Подробные regression logs сохранены в системном temp-каталоге
|
||||||
|
`wealthledger-sqlite-regression-pSyuoe`, durability —
|
||||||
|
`wealthledger-bank-durability-V0u1NA`. Эти локальные временные логи могут быть
|
||||||
|
удалены системой; воспроизводимые команды и
|
||||||
|
[матрица рисков](../../backend/README.md#реальная-sqlite-и-сохранность-данных)
|
||||||
|
остаются в backend. Исторические evidence ниже сохранены без переписывания.
|
||||||
|
|
||||||
|
### 2026-09-13 — P1-12: backend responsibilities
|
||||||
|
|
||||||
|
Статус: `done`. Backend commit `8ba0053`.
|
||||||
|
|
||||||
|
BankCards разделён на services карт, операций и категорий, сценарий сводки и
|
||||||
|
конкретный repository. Общая transaction/retry boundary, ownership, idempotency,
|
||||||
|
optimistic concurrency и ограничения возвратов сохранены. Stock/bond services
|
||||||
|
разделены по каталогу, рынку и доходам; связные operations и T-Bank client
|
||||||
|
сохранены. Business modules отделены от integrations; сложные modules используют
|
||||||
|
слои ответственности, компактные остаются плоскими.
|
||||||
|
|
||||||
|
- `npm run verify` — passed: lint, app/test typecheck, 35 suites / 257 tests, build.
|
||||||
|
- Восемь существующих HTTP/e2e suites выполнены временным runner
|
||||||
|
`/tmp/wl-modular-http.cjs`: отдельная SQLite DB на suite, migrations с
|
||||||
|
`RUST_LOG=info`, configured HTTP, 118 tests passed. OpenAPI совпадает с frontend
|
||||||
|
snapshot. Общий e2e blocker и статус задачи 03 этим не закрываются.
|
||||||
|
- Финальный перенос 47 файлов проверен сравнением 142 исходников/tests:
|
||||||
|
различий кроме относительных путей и форматирования нет.
|
||||||
|
- Formatter, docs `npm run verify`, lint backend conventions и `git diff --check`
|
||||||
|
прошли. Новые dependencies и architecture tests не добавлены.
|
||||||
|
|
||||||
|
### 2026-09-13 — P1-11: реальная SQLite
|
||||||
|
|
||||||
|
P1-11 поставлена в backend commit `7fde3ec`. Дополнено доказательство
|
||||||
|
WL-P2-03; статус общего e2e WL-P2-05 не меняется. Команды и матрица рисков —
|
||||||
|
[backend README](../../backend/README.md#реальная-sqlite-и-сохранность-данных).
|
||||||
|
|
||||||
|
- `npm run test:sqlite` — два последовательных успешных запуска после фиксации
|
||||||
|
loopback-listener: 8 auth и 21 карточный HTTP/SQLite test. Каждый suite получает
|
||||||
|
собственную временную DB; Prisma не мокируется. Барьер перед CAS гарантирует,
|
||||||
|
что оба refresh прочитали прежний hash; победившая сессия переживает replay.
|
||||||
|
- Существующие карточные tests покрывают owner links, idempotency/tombstones,
|
||||||
|
версии, конкурентные refund/archive/write, rollback source lock, атомарный
|
||||||
|
перевод, точные суммы и FK/unique constraints. Новая тестовая платформа не нужна.
|
||||||
|
- `npm run test:durability -- /tmp/p111-baseline` — PASS: migration, restart,
|
||||||
|
previous-app rollback, backup/restore и чтение сохранённой финансовой истории.
|
||||||
|
Baseline экспортирован из `8ec172f48eefa2237dc0c16919fbb2e726b39a65` и собран
|
||||||
|
отдельно. Его lockfile совпадает с текущим; при этой проверке переиспользованы
|
||||||
|
установленные node_modules. README также описывает независимый `npm ci`.
|
||||||
|
Runner проверяет наличие build и неизменённый префикс migration history.
|
||||||
|
- Node 22.21.0: `npm run verify` — PASS (35 suites, 257 unit tests, zero-warning
|
||||||
|
lint, оба typecheck, Prisma generate и build).
|
||||||
|
- Первые HTTP-запуски с автоматическим открытием/закрытием сервера Supertest
|
||||||
|
дали плавающий 404. Suites теперь держат один loopback-listener до `app.close()`;
|
||||||
|
повторные проверки прошли. Sandbox требует разрешения на локальный порт.
|
||||||
|
- Миграции выполнены с `RUST_LOG=info`. Это подтверждение выбранных DB-сценариев,
|
||||||
|
не снятие прежнего ограничения общего `test:e2e` и не выполнение задачи 03.
|
||||||
|
|
||||||
|
### 2026-09-13 — WL-P2-06: strict TypeScript
|
||||||
|
|
||||||
|
Поставка backend: commit `346a6f0` (`refactor: enforce strict backend typing`).
|
||||||
|
|
||||||
|
По продолжению P1-10 в [общем tsconfig](../../backend/tsconfig.json) включён
|
||||||
|
`strict: true` без отключения отдельных strict-проверок;
|
||||||
|
`noFallthroughCasesInSwitch` сохранён отдельно. Убраны избыточные настройки,
|
||||||
|
которые теперь включает `strict`.
|
||||||
|
|
||||||
|
После замечания владельца вариант с definite-assignment `!` заменён:
|
||||||
|
в 14 файлах 315 полей DTO объявлены через `declare`. Заполнение входных DTO
|
||||||
|
принадлежит Nest/class-transformer, ответные DTO описывают возвращаемые объекты.
|
||||||
|
`declare` не генерирует поля со значением `undefined` и не подменяет проверку
|
||||||
|
входных данных. Optional/nullable-типы, декораторы и валидация сохранены.
|
||||||
|
В [TbankClient](../../backend/src/integrations/tbank/tbank.client.ts)
|
||||||
|
`getOpenAccounts` и `getDividends` задают тип результата на `call<Response>`,
|
||||||
|
как соседние RPC; тип параметра `.then` выводится из этого результата.
|
||||||
|
|
||||||
|
В [ESLint](../../backend/eslint.config.mjs) включён
|
||||||
|
`@typescript-eslint/no-non-null-assertion: error`. Он не охватывает `field!: Type`,
|
||||||
|
поэтому `no-restricted-syntax` отдельно запрещает definite-assignment `!`
|
||||||
|
в свойствах и переменных. Устранены также 51 прежний non-null assertion и
|
||||||
|
четыре definite-assignment assertions в тестах: используются сужение типов,
|
||||||
|
локальные значения, типизированные результаты фильтрации и runtime assertions
|
||||||
|
в тестах. Транзакции, проверки доступа, cache generation и денежная точность
|
||||||
|
сохранены; отсутствующая категория в сводке приводит к явной внутренней ошибке.
|
||||||
|
|
||||||
|
Проверено на Node 22.21.0, backend `3cf883e` с указанными изменениями:
|
||||||
|
|
||||||
|
- `npm run verify` — passed: lint без warnings, strict app/test typechecks,
|
||||||
|
35 Jest suites / 257 tests, Prisma generation и production build.
|
||||||
|
- `DATABASE_URL=file:/tmp/wealthledger-declared-dto-openapi-20260913.db npm run test:openapi-contract`
|
||||||
|
— passed, 1 test; frontend snapshot не менялся.
|
||||||
|
- TypeScript Compiler API подтвердил все strict options в app/test/build/dev
|
||||||
|
configs. Каждый config отклоняет шесть примеров: неинициализированное поле,
|
||||||
|
несовместимый callback, `null` вместо строки, implicit `any`/`this`, доступ
|
||||||
|
к `unknown` в `catch` (TS2564, TS2345, TS2322, TS7006, TS2683, TS18046).
|
||||||
|
- Проверка ESLint через `lintText` подтверждает ошибки для `value!`, `field!`
|
||||||
|
и `let value!`; декларация DTO без `!` разрешена.
|
||||||
|
- Новые тесты ValidationPipe проходят до и после замены деклараций:
|
||||||
|
обязательные credentials, нормализация email, вложенные money DTO и
|
||||||
|
обязательный version с optional-полями `PartialType`. Дополнены проверки
|
||||||
|
инструментов без UID; отдельный тест проверяет имя категории и ошибку
|
||||||
|
при нарушенной ссылке в банковской сводке.
|
||||||
|
- `NODE_ENV=development NODE_OPTIONS=--experimental-vm-modules npx jest --config test/jest-e2e.json --runInBand bank-cards.e2e-spec.ts`
|
||||||
|
с новой `DATABASE_URL=file:/tmp/bank-cards-*/test.db` — passed, 21 tests,
|
||||||
|
включая реальные миграции, валидацию HTTP, права владельца, возвраты,
|
||||||
|
транзакции и перезапуск. Runner использует `RUST_LOG=info` для миграций.
|
||||||
|
Первый прогон дал 19 passed / 2 timeout в сценариях возвратов и потребовал
|
||||||
|
остановки оставшегося Jest-процесса. Повтор на новой базе прошёл 21/21;
|
||||||
|
исходный `3cf883e` в отдельной копии с теми же dependencies также прошёл
|
||||||
|
21/21. Это не доказательство стабильности общего e2e gate.
|
||||||
|
- Полный `strict` проходит; 317 diagnostics из предыдущей оценки устранены.
|
||||||
|
Общий DB/e2e gate и ограничение WL-P2-05 остаются отдельной задачей.
|
||||||
|
|
||||||
|
### 2026-09-13 — WL-P2-06: backend TypeScript
|
||||||
|
|
||||||
|
Выполнен пункт P1-10 списка инженерных улучшений от 13.09.2026
|
||||||
|
как продолжение существующего TypeScript gate. В [общем tsconfig](../../backend/tsconfig.json)
|
||||||
|
включены `noImplicitAny`, `strictBindCallApply`, `noFallthroughCasesInSwitch`;
|
||||||
|
app/test typechecks, production и dev build наследуют эти настройки.
|
||||||
|
Исходный код, зависимости, unsafe-checks и публичный API не менялись.
|
||||||
|
|
||||||
|
Проверено на Node 22.21.0, backend `3cf883e` с указанным изменением tsconfig:
|
||||||
|
|
||||||
|
- `npm run verify` до и после изменения — passed: zero-warning ESLint,
|
||||||
|
оба TypeScript gate, 33 Jest suites / 253 tests, Prisma generation и build.
|
||||||
|
- Проверка через TypeScript Compiler API с виртуальным ошибочным файлом для
|
||||||
|
каждого из четырёх configs — passed: implicit-any параметр даёт TS7006,
|
||||||
|
неверные аргументы `call`/`apply`/`bind` — TS2345/TS2322, проваливание `switch`
|
||||||
|
— TS7029. Контроль с выключенными тремя flags принимает тот же файл.
|
||||||
|
- `DATABASE_URL=file:/tmp/wealthledger-p1-10-openapi-20260913.db npm run test:openapi-contract`
|
||||||
|
— passed, 1 test; runtime OpenAPI совпал с frontend snapshot.
|
||||||
|
- `npx tsc --project tsconfig.typecheck.test.json --incremental false --strict --noImplicitAny --strictBindCallApply --noFallthroughCasesInSwitch --pretty false`
|
||||||
|
— оценка полного `strict`: 317 diagnostics, из них 315 TS2564 для полей DTO
|
||||||
|
и две TS2345 в `TbankClient.getOpenAccounts` / `getDividends` из-за callback
|
||||||
|
для `Promise<unknown>`. Полный `strict` пока не включён; это отдельный этап.
|
||||||
|
- Общий DB/e2e gate не запускался; WL-P2-05 сохраняет прежний статус.
|
||||||
|
|
||||||
|
### 2026-08-26 — SDD26-01–SDD26-03, SDD26-09, SDD26-12, SDD26-17, SDD26-21, SDD26-28–SDD26-30: done
|
||||||
|
|
||||||
|
Change синхронизирован в canonical specs и архивирован в [`2026-08-26-harden-openspec-sdd-quality`](../../specs/openspec/changes/archive/2026-08-26-harden-openspec-sdd-quality/).
|
||||||
|
|
||||||
|
- Delivery Map: frontend `N/A`, backend `N/A`, specs affected, docs affected; runtime OpenAPI contract gate `N/A`, потому что application runtime API и generated client не менялись.
|
||||||
|
- Затронутые Git-репозитории: specs `a8b2c4eed988f7e1a7b3d6b7188bbe6791f00e2a` (`feat: harden OpenSpec SDD workflow`), docs delivery `72c3a8a6683b95f7e734044fdabe568668097c33` (`docs: record SDD audit remediation`); terminal evidence фиксируется следующим docs commit.
|
||||||
|
- Pre-archive `bash specs/scripts/verify-workspace-change.sh harden-openspec-sdd-quality` под Node 22.21.0 — passed: Delivery Map verified frontend/backend `N/A`, specs/docs affected, runtime-openapi `N/A`; OpenSpec CLI 1.10.0 verified; planning coverage 9/9; schema valid; change valid; main specs 20/20; authority verified; docs `npm run verify` passed with 36 Markdown files and 0 issues.
|
||||||
|
- Controlled fixture suites: `bash specs/scripts/verify-agent-projection.test.sh` — passed at delivery time; follow-up cleanup later removed the projection layer. `bash specs/scripts/verify-openspec-authority.test.sh` — passed with expected controlled-negative failures.
|
||||||
|
- Archive: `openspec archive harden-openspec-sdd-quality -y --store wealth-ledger-specs` — updated `audit-remediation-workflow`, `engineering-change-governance`, `multi-repository-delivery` and `openspec-authority-and-lifecycle`; archived as `2026-08-26-harden-openspec-sdd-quality`.
|
||||||
|
- Post-archive verification: `openspec validate --specs --strict --store wealth-ledger-specs` — 20 passed, 0 failed; `bash specs/scripts/verify-openspec-authority.sh` — passed; `openspec schema validate wealth-ledger-sdd --verbose` — passed; `PATH=/Users/ksv741/.nvm/versions/node/v22.21.0/bin:$PATH npm run verify` in `docs` — passed.
|
||||||
|
- Semantic verification: Proposal, all 9 Delta Spec requirements, 28 scenarios, Design and 18/18 tasks matched implementation. Independent read-only review found no Critical or Important findings.
|
||||||
|
|
||||||
|
### 2026-08-24 — AUD24-01, AUD24-03–AUD24-09, A-14–A-16, A-18: done
|
||||||
|
|
||||||
|
Change синхронизирован в canonical specs и архивирован в [`2026-08-24-harden-agent-sdd-governance`](../../specs/openspec/changes/archive/2026-08-24-harden-agent-sdd-governance/).
|
||||||
|
|
||||||
|
- Delivery Map: frontend affected, backend affected, specs affected, docs affected; runtime OpenAPI contract gate required because frontend/backend contract verification pipelines changed. Workspace root `.opencode` projection and root `AGENTS.md` were updated locally; the workspace root is not a Git repository.
|
||||||
|
- Затронутые Git-репозитории: frontend `11be786943024699a4eccf91a8d1ad32a6d29e5f` (`chore: codify frontend local verification`), backend `2772d53647132e6119e411c6d18234db4301e541` (`chore: codify backend local verification`), specs `0006aea56207ad2192c678ced562feabf1a21c45` (`chore: harden SDD governance workflow`), docs delivery `1944f2fc970db2bdc292981e01b1aca549f9ac52` (`docs: record AI governance audit`); terminal evidence фиксируется следующим docs commit.
|
||||||
|
- `bash specs/scripts/verify-workspace-change.sh harden-agent-sdd-governance` под Node 22.21.0 — passed: Delivery Map verified for all four areas, OpenSpec CLI 1.10.0 verified, schema valid, change valid before archive, main specs 18/18 before sync, authority verified, frontend `npm run verify` passed (17 test files / 56 tests, coverage 82.57% statements / 76.08% branches / 77.35% functions / 83.48% lines, production build, initial JS 509,165 B), backend `npm run verify` passed (19 suites / 59 tests, build), docs `npm run verify` passed (34 Markdown files, 0 issues), runtime OpenAPI SHA-256 matched snapshot `96744da5f307b37ee3d9233b3259043425f65e49bcc60f78b46c585a78251db4`.
|
||||||
|
- После sync/archive `openspec validate --specs --strict --store wealth-ledger-specs` — 20 passed, 0 failed; `git diff --check` в frontend, backend, specs и docs — exit 0 before delivery commits.
|
||||||
|
- Semantic verification: Proposal, all 19 Delta Spec requirements, Design and 25/25 tasks matched implementation. Independent read-only re-review after fixes: Spec review 19/19, 0 Critical, 0 Important, 0 Minor semantic findings; Standards review final — no findings.
|
||||||
|
|
||||||
|
### 2026-08-24 — WL-P3-02, WL-P3-03, WL-P3-05, WL-P3-06: done
|
||||||
|
|
||||||
|
Change синхронизирован в canonical specs и архивирован в [`2026-08-24-tighten-frontend-quality-guardrails`](../../specs/openspec/changes/archive/2026-08-24-tighten-frontend-quality-guardrails/).
|
||||||
|
|
||||||
|
- Затронутые Git-репозитории: frontend `1fc269a` (`feat: enforce frontend quality guardrails`), specs `2029519` (`docs(specs): archive frontend quality guardrails`), docs `fa5db07` (`docs: activate frontend quality guardrails remediation`); terminal evidence фиксируется следующим docs commit.
|
||||||
|
- Frontend под Node 22.21.0: `npm run verify` — passed: OpenAPI and hygiene checks, strict TypeScript, Biome, 17 test files / 56 tests, coverage all committed `src` 82.57% statements / 76.08% branches / 77.35% functions / 83.48% lines, Knip, dependency gate с четырьмя fixtures, production build и bundle gate. Initial JavaScript payload — 509,165 B при budget 512,000 B; committed lazy baselines включают все dynamic route boundaries, включая `ProtectedLayout`.
|
||||||
|
- `openspec validate tighten-frontend-quality-guardrails --type change --strict --store wealth-ledger-specs` — passed до archive; после sync/archive `openspec validate --specs --strict --store wealth-ledger-specs` — 18 passed, 0 failed; `bash specs/scripts/verify-openspec-authority.sh` и `git diff --check` по затронутым repositories — passed.
|
||||||
|
- `opsx-verify` не установлен как shell executable; выполнена эквивалентная процедура из `.opencode/skills/openspec-verify-change/SKILL.md`: completeness 21/21 tasks, 3 requirements / 7 scenarios, correctness и coherence — без blocking findings. Independent read-only review нашёл три Important guardrail gaps; после fixes re-review — без Critical или Important findings.
|
||||||
|
- WL-P3-04 остаётся `planned` по явному решению owner: application accessibility semantics не входят в текущий MVP scope. Следующая единственная recommended задача — `tighten-backend-request-feedback` для WL-P3-01.
|
||||||
|
|
||||||
|
### 2026-08-24 — WL-P2-12, WL-P3-07, WL-P3-08: done
|
||||||
|
|
||||||
|
Change синхронизирован в canonical specs и архивирован в [`2026-08-24-address-dependency-advisories-and-repo-hygiene`](../../specs/openspec/changes/archive/2026-08-24-address-dependency-advisories-and-repo-hygiene/).
|
||||||
|
|
||||||
|
- Затронутые Git-репозитории: frontend `826936c` (`fix: harden frontend dependency and env hygiene`), backend `6052565` (`fix: harden backend runtime artifact hygiene`), specs `8897bd9` (`docs(specs): archive dependency hygiene remediation`), docs `60a1c33` (`docs: record dependency hygiene remediation evidence`).
|
||||||
|
- Frontend под Node 22: `npm run verify` — passed: OpenAPI check, hygiene check, strict TypeScript check, Biome, 17 test files / 55 tests, coverage, Knip, dependency check и production build. `npm audit --omit=dev` — 0 vulnerabilities; прямой `react-router` обновлён с 7.9.6 до 7.18.2 для GHSA-2w69-qvjg-hvjx.
|
||||||
|
- Backend под Node 22: `npm run hygiene:check`, `npm run lint`, `npm run typecheck:app`, `npm run typecheck:test` и `npm test -- --runInBand` — passed (19 suites, 59 tests). Runtime `dev.db` удалён из Git; intentional textual fixtures остаются versionable. `npm audit --omit=dev` всё ещё показывает три high по Prisma CLI path `prisma -> @prisma/config -> deepmerge-ts` для GHSA-ggr8-5vv4-36mx: public runtime path не найден, риск принят как tooling-only. Review trigger: совместимый stable Prisma release должен устранить `deepmerge-ts < 8.0.0`, либо появится доказуемый runtime call path.
|
||||||
|
- `npm run test:e2e` остаётся deferred по WL-P2-05: `prisma migrate deploy` на чистой temporary SQLite DB завершается пустым `Schema engine error` до Jest. Это не менялось данным change.
|
||||||
|
- `bash specs/scripts/verify-openspec-authority.sh` — passed; `openspec validate --specs --strict --store wealth-ledger-specs` — 17 passed, 0 failed; `git diff --check` по четырём репозиториям — passed. Эквивалентная `opsx-verify` процедура подтвердила 13/13 tasks, correctness и coherence. Независимый read-only review после исправлений — без Critical, Important или Minor findings.
|
||||||
|
|
||||||
|
### 2026-08-24 — WL-P2-10: done
|
||||||
|
|
||||||
|
Change синхронизирован в canonical specs и архивирован в [`2026-08-24-refresh-project-documentation-and-agent-entrypoints`](../../specs/openspec/changes/archive/2026-08-24-refresh-project-documentation-and-agent-entrypoints/).
|
||||||
|
|
||||||
|
- Затронутые Git-репозитории: frontend `be80ef2` (`docs: refresh frontend onboarding`), backend `fec542a` (`docs: refresh backend onboarding`), specs `9f68ae5` (`fix(specs): remove archived change duplicate` после archive commit `5b7bc0f`), docs `88c0cd3` (`docs: refresh project onboarding entrypoints`).
|
||||||
|
- `npm run verify` в frontend под Node 22 — passed: OpenAPI check, strict TypeScript check, Biome, 17 test files / 55 tests, coverage, Knip, dependency check и production build.
|
||||||
|
- Read-only link/path review — passed: 14 Markdown entrypoints без missing local links; `MVP.md` и `CONTEXT.md` удалены, а historical audit/archive references намеренно сохранены.
|
||||||
|
- `openspec validate --all --strict --store wealth-ledger-specs` — 16 passed, 0 failed; `bash specs/scripts/verify-openspec-authority.sh` — passed; `git diff --check` по затронутым путям — passed.
|
||||||
|
- Independent read-only standards/spec review выявил только Finder metadata и две неполные README links; metadata удалены, ссылки добавлены, re-review — passed. Эквивалентная `opsx-verify` процедура подтвердила completeness 16/16, correctness и coherence без blocking findings.
|
||||||
|
|
||||||
|
### 2026-08-24 — WL-P2-04, WL-P2-07, WL-P2-08: done
|
||||||
|
|
||||||
|
Change синхронизирован в canonical specs и архивирован в [`2026-08-24-align-api-contract-and-client-resilience`](../../specs/openspec/changes/archive/2026-08-24-align-api-contract-and-client-resilience/).
|
||||||
|
|
||||||
|
- Затронутые Git-репозитории: frontend `6d892da` (`fix: preserve cached data during refresh failures`), backend `9bd3400` (`fix: align auth success status contract`), specs `800450d` (`docs(specs): archive api contract resilience remediation`), docs `b91280c` (`docs: document auth and client refresh resilience`).
|
||||||
|
- Frontend под Node 22: `npm run verify` — passed: OpenAPI check, strict TypeScript check, Biome, 17 test files / 55 tests, coverage, Knip, dependency check и production build.
|
||||||
|
- Backend под Node 22: `npm run lint`, `npm run typecheck:app`, `npm run typecheck:test` и `npm test -- --runInBand` — passed (19 suites, 59 tests). `test:e2e` не запускался: WL-P2-05 остаётся deferred из-за Prisma migration gate.
|
||||||
|
- `bash specs/scripts/verify-runtime-openapi.sh` — passed: runtime и snapshot SHA-256 `96744da5f307b37ee3d9233b3259043425f65e49bcc60f78b46c585a78251db4` совпали. `openspec validate --specs --strict --store wealth-ledger-specs` — 16 passed, 0 failed; authority verifier и `git diff --check` — passed.
|
||||||
|
- `opsx-verify` выполнен эквивалентной процедурой из `.opencode/skills/openspec-verify-change/SKILL.md`: completeness 16/16 tasks, correctness и coherence — без blocking findings. Независимый read-only review не нашёл Critical, Important или Minor issues.
|
||||||
|
|
||||||
|
### 2026-08-23 — WL-P2-01, WL-P2-02, WL-P2-03: done
|
||||||
|
|
||||||
|
Change синхронизирован в main specs и архивирован в [`specs/openspec/changes/archive/2026-08-23-harden-auth-and-cache-concurrency`](../../specs/openspec/changes/archive/2026-08-23-harden-auth-and-cache-concurrency/).
|
||||||
|
|
||||||
|
- Затронутые Git-репозитории: backend `c77b3cf` (`fix: harden auth and cache concurrency`), specs `6e20cdf` (`docs(specs): archive auth and cache remediation`), docs `8568e7b` (`docs: record auth and cache concurrency policy`).
|
||||||
|
- Backend под Node 22: `npm run lint`, `npm run typecheck:app`, `npm run typecheck:test` и `npm test -- --runInBand` — passed (18 suites, 56 tests). `test:e2e` не запускался: WL-P2-05 остаётся deferred из-за Prisma migration gate.
|
||||||
|
- `openspec validate harden-auth-and-cache-concurrency --type change --strict --store wealth-ledger-specs` — passed до archive; после sync `openspec validate --specs --strict --store wealth-ledger-specs` — 15 passed, 0 failed; authority verifier и `git diff --check` — passed.
|
||||||
|
- `opsx-verify` не установлен как shell executable; выполнена эквивалентная процедура из `.opencode/skills/openspec-verify-change/SKILL.md`: completeness 12/12 tasks, correctness и coherence — без blocking findings. Independent read-only review не нашёл Critical/Important findings.
|
||||||
|
|
||||||
|
### 2026-08-23 — WL-P1-05, WL-P2-06, WL-P2-09, WL-P2-11: done
|
||||||
|
|
||||||
|
Change архивирован в [`specs/openspec/changes/archive/2026-08-23-make-local-verification-agent-safe`](../../specs/openspec/changes/archive/2026-08-23-make-local-verification-agent-safe/).
|
||||||
|
|
||||||
|
- Затронутые Git-репозитории: frontend `7a3048c` (`docs: document runtime OpenAPI verification`), backend `71d57d6` (`fix: make backend local verification safe`), specs `72444db` (`docs(specs): archive local verification remediation`), docs `293df9c` (`docs: document local verification workflow`).
|
||||||
|
- Backend под Node 22: `npm run lint`, `npm run typecheck:app`, `npm run typecheck:test` и `npm test -- --runInBand` — passed (17 suites, 51 tests).
|
||||||
|
- `npm run openapi:check` в frontend — passed; `bash specs/scripts/verify-runtime-openapi.sh` — runtime и snapshot SHA-256 совпали.
|
||||||
|
- `openspec validate make-local-verification-agent-safe --type change --strict --store wealth-ledger-specs` перед archive — passed; после sync `openspec validate --specs --strict --store wealth-ledger-specs` — 15 passed, 0 failed; authority verifier, `git diff --check` и independent read-only review — passed/no findings.
|
||||||
|
|
||||||
|
### 2026-08-23 — WL-P2-05, WL-P2-13: deferred
|
||||||
|
|
||||||
|
Под Node 22 `prisma migrate deploy` 7.9.1 несколько раз завершается пустым
|
||||||
|
`Schema engine error` на новых SQLite DB до запуска Jest; при включении engine
|
||||||
|
diagnostics результат меняется. `npm view prisma version` вернул ту же
|
||||||
|
published stable версию 7.9.1. Поэтому не вводится параллельный migration runner
|
||||||
|
и не создаётся общий workspace verdict с ложным зелёным статусом. Review trigger
|
||||||
|
для обоих findings: следующая stable Prisma должна трижды подряд применить
|
||||||
|
существующие migrations к чистой SQLite DB на Node 22.
|
||||||
|
|
||||||
|
### 2026-08-23 — WL-P1-03, WL-P1-04: `harden-openspec-authority-and-lifecycle`
|
||||||
|
|
||||||
|
Статус: `done`. Change синхронизирован в main specs и архивирован в [`specs/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle`](../../specs/openspec/changes/archive/2026-08-23-harden-openspec-authority-and-lifecycle/).
|
||||||
|
|
||||||
|
- Затронутые Git-репозитории: frontend `d870d53` (`docs: define canonical OpenSpec store`); backend `fee4f8c` (`docs: define canonical OpenSpec store`); specs `51344d4` (`docs(specs): harden OpenSpec authority lifecycle`); docs `0bc8d1c` (`docs: document OpenSpec authority lifecycle`).
|
||||||
|
- `bash specs/scripts/verify-openspec-authority.test.sh` — passed: positive fixture, второй OpenSpec root, implicit/conditional store selection, parenthesized invocation, archive override и framework planning location корректно отклоняются; `references/**` исключён.
|
||||||
|
- `bash specs/scripts/verify-openspec-authority.sh` из workspace root и всех четырёх независимых repositories — `OpenSpec authority verified: wealth-ledger-specs at specs/openspec`.
|
||||||
|
- `openspec validate harden-openspec-authority-and-lifecycle --type change --strict --store wealth-ledger-specs` — passed перед archive; `openspec validate --specs --strict --store wealth-ledger-specs` — 15 passed, 0 failed после sync/archive.
|
||||||
|
- `git diff --check` в frontend, backend, specs и docs — exit 0 перед commits; независимый read-only review scope — Critical/Important findings отсутствуют.
|
||||||
|
|
||||||
|
### 2026-08-23 — WL-P1-01, WL-P1-02: `stabilize-frontend-query-and-strict-contracts`
|
||||||
|
|
||||||
|
Статус: `done`. OpenSpec change синхронизирован в main specs и архивирован в [`specs/openspec/changes/archive/2026-08-23-stabilize-frontend-query-and-strict-contracts`](../../specs/openspec/changes/archive/2026-08-23-stabilize-frontend-query-and-strict-contracts/).
|
||||||
|
|
||||||
|
- Затронутые Git-репозитории: frontend commit `1250581` (`fix: stabilize frontend query cache contracts`); specs commit `b8a3f96` (`docs(specs): archive frontend query remediation`).
|
||||||
|
- `npm run verify` в `frontend` — passed: OpenAPI check, strict TypeScript check, Biome, 16 test files / 48 tests, coverage, Knip, dependency check и production build.
|
||||||
|
- `openspec validate stabilize-frontend-query-and-strict-contracts --type change --store wealth-ledger-specs` — `Change 'stabilize-frontend-query-and-strict-contracts' is valid` перед archive.
|
||||||
|
- `openspec validate --specs --store wealth-ledger-specs` — 14 specs passed, 0 failed перед archive.
|
||||||
|
- `git diff --check` для staged OpenSpec archive/sync changes — exit 0.
|
||||||
|
- Archive выполнен по явному разрешению владельца с одной незакрытой только административной задачей 4.2: она требует archive path и поэтому не могла быть закрыта до archive. Сразу после archive задача отмечена `[x]`; WL-P1-04 теперь `active` в `harden-openspec-authority-and-lifecycle`, чтобы убрать эту циклическую зависимость из будущего процесса.
|
||||||
|
|
||||||
|
### 2026-08-23 — bootstrap verification: `establish-audit-remediation-workflow`
|
||||||
|
|
||||||
|
Этот process change не закрывает Audit ID. Перед archive выполнены следующие read-only checks:
|
||||||
|
|
||||||
|
- `cmp -s audit-20-08-2026.md docs/audits/audit-20-08-2026.md` — exit 0; versioned snapshot byte-for-byte совпадает с исходной локальной копией.
|
||||||
|
- `openspec validate establish-audit-remediation-workflow --type change --store wealth-ledger-specs` — `Change ... is valid`.
|
||||||
|
- Структурная проверка registry — 26 records, 26 `planned`, 0 `active`, 0 `done`, одна «Следующая задача»; все четыре agent pointers и оба registry links разрешаются.
|
||||||
|
- `git diff --check` в frontend, backend, specs и docs — exit 0.
|
||||||
|
- Независимый read-only review registry-to-audit mapping, OpenSpec links, instruction discoverability и scope diff — `findings: none`.
|
||||||
|
- `opsx-verify` доступен как OpenCode command, но не установлен как shell executable в этой среде; выполнена эквивалентная процедура из `.opencode/skills/openspec-verify-change/SKILL.md` с теми же dimensions completeness, correctness и coherence.
|
||||||
|
- Change синхронизирован в main specs и архивирован в [`specs/openspec/changes/archive/2026-08-23-establish-audit-remediation-workflow`](../../specs/openspec/changes/archive/2026-08-23-establish-audit-remediation-workflow/).
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,88 @@
|
|||||||
|
# Дизайн-система WealthLedger
|
||||||
|
|
||||||
|
Приложение и UI-каталог используют одну визуальную основу: цветовые роли,
|
||||||
|
типографику, интервалы, радиусы, тени и адаптивные размеры. Общая тема основана
|
||||||
|
на Mantine; её значения не дублируются отдельно в каталоге.
|
||||||
|
Поддерживается светлая схема, тёмная тема не заявлена.
|
||||||
|
|
||||||
|
## Тема и токены
|
||||||
|
|
||||||
|
Опубликованная система включает восемь палитр по десять оттенков: blue, gray,
|
||||||
|
dark, red, green, yellow, grape и orange; базовые white/black, оттенки
|
||||||
|
по умолчанию и семантические роли. Общая гарнитура применяется и к заголовкам,
|
||||||
|
стандартный радиус — средний. Настройки являются доверенной локальной частью
|
||||||
|
приложения; загрузка внешних или пользовательских токенов не предусмотрена.
|
||||||
|
|
||||||
|
| Роль | Цветовая основа | Применение |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| canvas | gray.2 | Фон приложения |
|
||||||
|
| surface | white | Поверхности и формы |
|
||||||
|
| subtle | gray.0 | Приглушённая поверхность |
|
||||||
|
| text | dark.7 | Основной текст |
|
||||||
|
| muted | gray.6 | Вторичный текст |
|
||||||
|
| border | gray.3 | Граница поверхности |
|
||||||
|
| primary | blue filled | Основное действие и активная навигация |
|
||||||
|
| info | blue.6 | Информация |
|
||||||
|
| positive | green.6 | Положительный результат с текстом или знаком |
|
||||||
|
| negative | red.6 | Ошибка или отрицательный результат с текстом или знаком |
|
||||||
|
| warning | yellow.6 | Предупреждение; не мелкий текст на белом |
|
||||||
|
|
||||||
|
Цвет не заменяет подпись или знак. Палитра не гарантирует доступный контраст
|
||||||
|
произвольного сочетания текста и фона. Дополнительные grape/orange и отдельные
|
||||||
|
оформления карточек сохраняют предметный смысл соответствующего экрана;
|
||||||
|
они не образуют универсальную финансовую классификацию всей системы.
|
||||||
|
|
||||||
|
Общие шкалы основаны на принятых значениях приложения и Mantine 9.5.1.
|
||||||
|
Обновление библиотеки не заменяет их автоматически. Масштабируемые размеры,
|
||||||
|
тени и адаптивные границы требуют сохранения единиц и визуальной проверки.
|
||||||
|
Отдельные градиенты, тени и размеры экранов остаются локальными визуальными
|
||||||
|
решениями; переход всех существующих значений в общую шкалу не завершён.
|
||||||
|
|
||||||
|
Точные значения определены в [токенах приложения](../../frontend/src/shared/theme/tokens.json),
|
||||||
|
согласованное поведение — в [спецификации дизайн-системы](../../specs/openspec/specs/frontend-design-system/spec.md).
|
||||||
|
Эта страница объясняет их смысл, не вводя отдельную систему значений.
|
||||||
|
|
||||||
|
## Переносимый JSON
|
||||||
|
|
||||||
|
Опубликованные токены представлены в собственном формате WealthLedger,
|
||||||
|
версия 1. Это данные без исполняемого кода и зависимостей от runtime приложения.
|
||||||
|
Совместимость с DTCG или импортом конкретного инструмента не заявлена.
|
||||||
|
|
||||||
|
| Данные | Представление |
|
||||||
|
| --- | --- |
|
||||||
|
| Цвета | Hex, палитры оттенков 0–9 и именованные базовые цвета |
|
||||||
|
| Семантические роли | Ссылка на цвет и оттенок, описание; отсутствие оттенка палитры означает основной оттенок светлой схемы |
|
||||||
|
| Типографика | Наборы шрифтов, размеры в px, числовые веса и безразмерная высота строки |
|
||||||
|
| Интервалы и радиусы | Именованные шкалы xs–xl в px и выбранный радиус по умолчанию |
|
||||||
|
| Тени | Упорядоченные слои: смещения, размытие и растяжение в px, базовый цвет и непрозрачность |
|
||||||
|
| Адаптивные границы | Именованные размеры xs–xl в em, независимые от масштаба интерфейса |
|
||||||
|
|
||||||
|
Перенос в другую систему требует сопоставления единиц, шрифтов, слоёв теней
|
||||||
|
и семантических ссылок. Само чтение JSON не обеспечивает такой совместимости.
|
||||||
|
Несовместимое изменение формата требует новой версии; внешняя интеграция,
|
||||||
|
генератор артефактов и публикация пакета пока не реализованы.
|
||||||
|
|
||||||
|
## Общие компоненты
|
||||||
|
|
||||||
|
| Представление | Поведение и ограничения |
|
||||||
|
| --- | --- |
|
||||||
|
| Пустой результат или ошибка | Заголовок, пояснение и необязательное действие; ошибка объявляется вспомогательным технологиям, кнопка имеет доступное название |
|
||||||
|
| Загрузка | Доступная область состояния занятости с пояснением и декоративными фигурами без фокуса; структура отражает ожидаемый экран |
|
||||||
|
| Действия | Основной, вторичный и вспомогательный варианты; состояния выполнения и недоступности |
|
||||||
|
|
||||||
|
Общие представления не получают финансовые данные и не управляют
|
||||||
|
аутентификацией. Состояние выполнения действия отличается от загрузки всей
|
||||||
|
страницы. При фоновом обновлении доступные данные сохраняются с локальным
|
||||||
|
индикатором; правила восстановления описаны в
|
||||||
|
[UI resilience](../architecture/cross-cutting/ui-resilience.md).
|
||||||
|
|
||||||
|
Каркас с продуктовой навигацией не входит в заявленный общий набор.
|
||||||
|
Предметные карточки и композиции представлены в [UI-каталоге](ui-catalog.md).
|
||||||
|
|
||||||
|
## Границы доказательств
|
||||||
|
|
||||||
|
Каталог использует вымышленные данные и локальные действия без входа,
|
||||||
|
сохранённого пользовательского состояния и application API. Проверки отделяют
|
||||||
|
его от исполнения продуктовых запросов. Вид, размеры, фокус и взаимодействия
|
||||||
|
проверяются в браузере; каталог не доказывает восстановление реального API
|
||||||
|
или прохождение инвестиционного сценария.
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# Принципы изменения и проверки системы
|
||||||
|
|
||||||
|
Ожидаемый результат определяется задачей пользователя. Сопровождение сохраняет
|
||||||
|
ответственность подсистем, безопасность, точность данных и проверяемость результата.
|
||||||
|
OpenSpec остаётся [необязательным средством согласования](openspec-authority-and-lifecycle.md);
|
||||||
|
исторические свидетельства не становятся обязательной процедурой новой задачи.
|
||||||
|
|
||||||
|
## Выбор реализации
|
||||||
|
|
||||||
|
Связное поведение принадлежит ответственной подсистеме. Существующая возможность
|
||||||
|
используется повторно, если сохраняет нужный смысл и инварианты. Новая граница
|
||||||
|
оправдана самостоятельной ответственностью, общей потребностью нескольких
|
||||||
|
потребителей либо необходимостью отделить доступ и целостность данных.
|
||||||
|
|
||||||
|
Простота оценивается по ясности всей системы и стоимости сопровождения,
|
||||||
|
а не по размеру изменения. Предполагаемая будущая возможность сама по себе
|
||||||
|
не оправдывает новую подсистему. Упрощение сохраняет валидацию, доступность,
|
||||||
|
обработку ошибок и защиту от потери данных.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
При выборе внешней зависимости учитываются текущая потребность, совместимость,
|
||||||
|
поддержка, безопасность и стоимость сопровождения. Использование принятой
|
||||||
|
платформы может быть проще собственной реализации; зависимость ради одной
|
||||||
|
воображаемой будущей возможности не является самостоятельной потребностью.
|
||||||
|
Новый сервис или общая инфраструктура должны решать актуальную задачу.
|
||||||
|
|
||||||
|
## Risk-based testing
|
||||||
|
|
||||||
|
Изменения безопасности и приватности, денежных расчётов, целостности данных,
|
||||||
|
миграций, конкурентного доступа, публичного контракта, критического сценария
|
||||||
|
и подтверждённой регрессии требуют автоматической проверки соответствующего
|
||||||
|
риска на наблюдаемой потребителем границе.
|
||||||
|
|
||||||
|
Статические проверки не доказывают финансовую или функциональную корректность.
|
||||||
|
Покрытие frontend служит ограничителем регрессий; для backend общего числового
|
||||||
|
порога нет. Проверки поведения библиотеки или проценты сами по себе не заменяют
|
||||||
|
проверку собственного инварианта системы.
|
||||||
|
|
||||||
|
## Проверка результата
|
||||||
|
|
||||||
|
Результат оценивается по исходной задаче: сохранены ли смысл данных и контракты,
|
||||||
|
правильно ли разграничен доступ, учтены ли ошибки и доступность интерфейса.
|
||||||
|
Проверки приложений и документации дополняются
|
||||||
|
[согласованием HTTP-контракта](runtime-openapi-verification.md), если он меняется.
|
||||||
|
Общего CI сейчас нет.
|
||||||
|
|
||||||
|
Успешная проверка применима к тому состоянию системы и тем предпосылкам,
|
||||||
|
на которых выполнена. При их изменении старое свидетельство не доказывает
|
||||||
|
новый результат. Автоматическое исправление материалов и проверка без их
|
||||||
|
изменения — разные действия.
|
||||||
|
|
||||||
|
[Влияние на документацию](../CONTRIBUTING.md#обновление-вместе-с-кодом) входит
|
||||||
|
в оценку результата: описанные понятия, сценарии, решения и ограничения
|
||||||
|
обновляются вместе с системой. Отсутствие влияния может быть обоснованным
|
||||||
|
результатом. Отчёт о поставке различает выполненное, проверенное и остающиеся
|
||||||
|
ограничения; количество формальных отметок не заменяет смысловую проверку.
|
||||||
|
|
||||||
|
## Guardrails и local gates
|
||||||
|
|
||||||
|
Автоматические проверки гарантируют только свойства, охваченные их областью.
|
||||||
|
Смысловые требования — например, правильный владелец данных, авторизация,
|
||||||
|
локализация и полезность выбранных проверок — требуют отдельной оценки.
|
||||||
|
Предпочтение между равноценными вариантами отличается от обязательного
|
||||||
|
инварианта. Новая автоматизация оправдана реальным риском или повторяющимся
|
||||||
|
дефектом; её наличие само по себе не является целью.
|
||||||
|
|
||||||
|
## UI-решения
|
||||||
|
|
||||||
|
Ожидаемые экраны, состояния, взаимодействия и поведение на разных размерах
|
||||||
|
экрана определяются пользовательским сценарием. [Каталог](ui-catalog.md)
|
||||||
|
и [дизайн-система](design-system.md) показывают существующую визуальную основу.
|
||||||
|
Неоднозначный вариант можно оценить по конкретному примеру до реализации.
|
||||||
|
Успешная сборка каталога не доказывает доступность и визуальное соответствие.
|
||||||
|
|
||||||
|
## Изменение опасного контракта
|
||||||
|
|
||||||
|
Для публичного контракта, безопасности, данных, платежей и труднообратимого
|
||||||
|
изменения существенная неопределённость разрешается до применения решения.
|
||||||
|
Для миграции важны условия отката, сохранность уже созданных данных и проверка
|
||||||
|
восстановления. Порядок разрешения конфликтов знаний описан в
|
||||||
|
[контексте системы](agent-token-efficiency.md#источники-и-конфликты).
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
# Локальное окружение и восстановление
|
||||||
|
|
||||||
|
WealthLedger поставляется как локальное приложение: frontend работает
|
||||||
|
в браузере, backend обслуживает API и обращается к SQLite и внешним источникам.
|
||||||
|
Это описание предпосылок и ограничений работы, без привязки к устройству
|
||||||
|
исходного кода. Описано локальное окружение; production-топология по нему
|
||||||
|
не подтверждается.
|
||||||
|
|
||||||
|
## Версия Node
|
||||||
|
|
||||||
|
Все локальные проверки используют Node 24 LTS; `.nvmrc` в frontend, backend
|
||||||
|
и docs фиксирует 24.21.0. Specs использует frontend `.nvmrc`.
|
||||||
|
По [официальному расписанию Node](https://github.com/nodejs/Release#release-schedule)
|
||||||
|
поддержка Node 24 завершается 30.04.2028.
|
||||||
|
В каждом npm-репозитории выполните `nvm install`, `nvm use --silent` и `npm ci`;
|
||||||
|
в backend затем `npm run build` для генерации Prisma client.
|
||||||
|
Поставляемый npm 11.19 запускает dependency install scripts только по
|
||||||
|
`allowScripts` в package.json; разрешения привязаны к версиям сборочных
|
||||||
|
зависимостей и пересматриваются при их обновлении.
|
||||||
|
После смены major обязательна переустановка native modules через `npm ci`.
|
||||||
|
Для отката верните прежние runtime/package/lockfile-конфигурации, выберите Node 22
|
||||||
|
и повторите `npm ci` и backend build. Сам переход Node не меняет схему или данные.
|
||||||
|
|
||||||
|
## Подготовка backend
|
||||||
|
|
||||||
|
Локальная серверная среда использует Node 24 и SQLite. Для работы нужны:
|
||||||
|
|
||||||
|
| Параметр | Назначение и ограничение |
|
||||||
|
| --- | --- |
|
||||||
|
| Адрес локальной базы | Одинаковая база используется при подготовке схемы и работе приложения; относительный адрес зависит от рабочей директории процесса |
|
||||||
|
| Разрешённый origin frontend | Определяет browser-источник, которому разрешён доступ к API с credentials |
|
||||||
|
| Порт API | По умолчанию 3000 |
|
||||||
|
| Секрет подписи сессий | Секрет не короче 32 символов; отличается от ключа шифрования подключения |
|
||||||
|
| Ключ шифрования подключения | 32 байта в base64; требуется для чтения сохранённого T-Bank token |
|
||||||
|
| Параметры интеграции | Адрес T-Bank, лимит запросов и при необходимости доверенный CA; [ограничения](../integrations/tbank.md#конфигурация-и-ограничения) |
|
||||||
|
|
||||||
|
T-Bank token вводится пользователем через интерфейс после входа и не является
|
||||||
|
общим параметром окружения. Существующая база и её ключ шифрования сохраняются
|
||||||
|
при обновлении. До миграции необходима резервная копия; восстановление проверяется
|
||||||
|
на отдельной копии. Потеря или замена ключа не является безвредным сбросом:
|
||||||
|
прежнее подключение нельзя расшифровать новым ключом.
|
||||||
|
|
||||||
|
После подготовки зависимостей и конфигурации примените штатные migrations:
|
||||||
|
из backend после `nvm use --silent` выполните `npx prisma migrate deploy`.
|
||||||
|
Для существующей базы сначала сохраните согласованный backup.
|
||||||
|
На Node 22.21.0 / Prisma 7.9.1 обнаружена зависимость fresh migration от
|
||||||
|
унаследованного `RUST_LOG=warn/error`. Backend Prisma config исключает эту
|
||||||
|
переменную из процесса CLI; назначать `RUST_LOG=info` больше не требуется.
|
||||||
|
Условия и доказательство — в
|
||||||
|
[сквозной проверке](verification.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](../domains/authentication/overview.md).
|
||||||
|
|
||||||
|
## Первый сценарий
|
||||||
|
|
||||||
|
1. Пользователь регистрируется или входит. Новый профиль получает сессию;
|
||||||
|
после перезагрузки приложение пытается её восстановить.
|
||||||
|
2. В настройках пользователь сохраняет собственный T-Bank Invest token.
|
||||||
|
Успех означает проверенный доступ к брокеру и сохранённое подключение.
|
||||||
|
3. В разделе «Счета» доступны отдельный и общий портфель. Отсутствие доступных
|
||||||
|
счетов является допустимым пустым результатом.
|
||||||
|
4. «Доходы» показывают полученные выплаты, «События» — будущие.
|
||||||
|
5. При выходе локальная сессия и её клиентский кэш очищаются.
|
||||||
|
|
||||||
|
Без T-Bank token доступны регистрация, вход, состояние отсутствующего
|
||||||
|
подключения и [ручной банковский учёт](../domains/cards/overview.md).
|
||||||
|
Банковский сценарий не подтверждает доступность инвестиционных поставщиков.
|
||||||
|
|
||||||
|
## Сохранение данных и откат
|
||||||
|
|
||||||
|
Перед обновлением сохраняется согласованный SQLite backup; ключ шифрования
|
||||||
|
T-Bank хранится отдельно. Для работающей базы используется SQLite backup API,
|
||||||
|
который учитывает WAL. Несогласованное копирование файлов базы и WAL не является
|
||||||
|
резервной копией. Восстановление сначала проверяется на отдельном файле.
|
||||||
|
|
||||||
|
Банковская миграция добавляет таблицы, сохраняя User, Session и TbankConnection.
|
||||||
|
При необходимости отката приложения сначала прекращаются новые записи и
|
||||||
|
сохраняется актуальный снимок. Затем возвращается совместимая предыдущая версия
|
||||||
|
backend при сохранении расширенной схемы. Старый backup не восстанавливается
|
||||||
|
поверх новой истории: это удалило бы введённые банковские операции.
|
||||||
|
|
||||||
|
Совместимость проверена с исходной версией backend
|
||||||
|
`8ec172f48eefa2237dc0c16919fbb2e726b39a65`: она выполняла вход с расширенной
|
||||||
|
схемой, а новая версия после возврата читала прежние карты и операции.
|
||||||
|
Отдельно проверены перезапуск, служебные данные защиты повторов и приложение
|
||||||
|
на восстановленной SQLite-копии. Это подтверждение конкретной пары версий,
|
||||||
|
а не гарантия совместимости любого будущего изменения схемы.
|
||||||
|
|
||||||
|
Проверка воспроизводится через
|
||||||
|
[процессный сценарий](../../backend/test/bank-cards-durability.cjs)
|
||||||
|
с каталогом отдельно собранного baseline: после активации backend `.nvmrc`
|
||||||
|
выполнить `node test/bank-cards-durability.cjs <baseline-directory>`.
|
||||||
|
Сценарий создаёт собственные временные базы и вымышленные данные. Migrations
|
||||||
|
и HTTP/DB-ограничения описаны в [проверке системы](verification.md).
|
||||||
|
|
||||||
|
## Если запуск или сценарий не работает
|
||||||
|
|
||||||
|
| Симптом | Проверка и безопасное восстановление |
|
||||||
|
| --- | --- |
|
||||||
|
| Порт занят | Адрес действующего процесса или выбранный свободный порт должен совпадать с настройками клиента и разрешённым origin |
|
||||||
|
| Ошибка CORS или сессия теряется после reload | Согласованность адресов, hostname и режима cookie проверяется на стороне browser и backend |
|
||||||
|
| Ошибка подготовки базы | Сохранить версию среды и текст ошибки; использовать backend Prisma config, не удалять пользовательскую базу и не обходить migrations |
|
||||||
|
| `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).
|
||||||
|
|
||||||
|
## Проверка первого изменения
|
||||||
|
|
||||||
|
Успешная сборка, запуск и прохождение сценария — разные доказательства.
|
||||||
|
Границы [проверок системы](verification.md) и
|
||||||
|
[HTTP-контракта](runtime-openapi-verification.md) рассматриваются отдельно.
|
||||||
|
Завершённость результата учитывает фактические проверки и остающиеся ограничения.
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# OpenSpec: роль требований и история процесса
|
||||||
|
|
||||||
|
Обязательный SDD lifecycle отменён для обычной разработки согласно
|
||||||
|
[ADR 012](../adr/012-task-driven-development-with-scoped-context.md).
|
||||||
|
Задача пользователя определяет ожидаемый результат; дополнительные planning
|
||||||
|
artifacts и архивирование не являются условием её завершения.
|
||||||
|
|
||||||
|
## Существующие материалы
|
||||||
|
|
||||||
|
Основные спецификации сохраняют согласованное проверяемое поведение, включая
|
||||||
|
текущее. OpenSpec changes содержат предложения, их дизайн и задачи, архив —
|
||||||
|
историю согласования. Store `wealth-ledger-specs` находится в `specs/openspec/`:
|
||||||
|
основные спецификации — `specs/`, изменения — `changes/` внутри Store.
|
||||||
|
[Предметный индекс](../../specs/AI_INDEX.md) ведёт к конкретным requirements/scenarios.
|
||||||
|
|
||||||
|
Существующее описание изменения относится только к своему объёму и не доказывает
|
||||||
|
готовность возможности. Docs объясняет систему и последствия правил;
|
||||||
|
при расхождении со спецификацией нужно установить причину, а не объявлять
|
||||||
|
реализацию или текст автоматически верным.
|
||||||
|
|
||||||
|
## Если OpenSpec запрошен явно
|
||||||
|
|
||||||
|
OpenSpec остаётся необязательным средством согласования требований, дизайна
|
||||||
|
и задач. Его собственная модель определяет состав и порядок артефактов;
|
||||||
|
подтверждение человеком и смысловая проверка результата не выводятся из
|
||||||
|
одного наличия файлов. Обновление требований и архивирование меняют историю,
|
||||||
|
поэтому относятся к явно выбранной работе с этой историей.
|
||||||
|
|
||||||
|
Причины прежнего процесса и его упрощения сохранены в
|
||||||
|
[ADR 010](../adr/010-native-openspec-sdd-and-local-orchestration.md) и
|
||||||
|
[ADR 011](../adr/011-native-openspec-lifecycle-with-repository-gates.md).
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# Проверка runtime OpenAPI
|
||||||
|
|
||||||
|
## Назначение
|
||||||
|
|
||||||
|
Проверка сравнивает OpenAPI, сформированный настроенным backend-приложением,
|
||||||
|
с описанием API, на котором основан frontend. Backend остаётся единственным
|
||||||
|
источником исполняемого HTTP-контракта; frontend-клиент является производным.
|
||||||
|
Причины выбора описаны в [ADR 002](../adr/002-nest-generated-openapi-contract.md).
|
||||||
|
|
||||||
|
Эта проверка подтверждает согласованность публичного контракта, но не заменяет
|
||||||
|
проверки каждого приложения и наблюдаемого HTTP-поведения.
|
||||||
|
|
||||||
|
## Выбор и направление изменения
|
||||||
|
|
||||||
|
Изменением контракта считается изменение маршрута, входного или выходного поля,
|
||||||
|
статуса либо формы ошибки. Внутреннее изменение представления без изменения
|
||||||
|
API не требует нового описания контракта.
|
||||||
|
|
||||||
|
Направление согласования: публичный API backend → OpenAPI → производное
|
||||||
|
описание и типизированный клиент frontend. Две независимо редактируемые версии
|
||||||
|
контракта создавали бы риск расхождения. Перед изменением, влияющим на
|
||||||
|
совместимость, учитываются существующие потребители и сохранность данных.
|
||||||
|
Семантика запросов и ошибок описана в [HTTP API](../architecture/http-api.md).
|
||||||
|
|
||||||
|
## Границы и результат
|
||||||
|
|
||||||
|
Сравнение выполняется на границе настроенного приложения, нормализует порядок
|
||||||
|
ключей JSON-объектов и сохраняет порядок массивов. Оно не обновляет сравниваемые
|
||||||
|
описания и клиент автоматически и не обращается к T-Bank или другим внешним
|
||||||
|
источникам. Несовпадение указывает на рассогласование контракта.
|
||||||
|
|
||||||
|
Равенство схем не доказывает фактическую работу валидации, авторизации,
|
||||||
|
статусов, cookie и обработки ошибок: для этого нужны наблюдения полного
|
||||||
|
HTTP-потока. Согласованность описаний и функциональная корректность —
|
||||||
|
разные доказательства.
|
||||||
|
|
||||||
|
## Граница e2e
|
||||||
|
|
||||||
|
`npm run test:openapi-contract` подготавливает отдельную временную SQLite-базу
|
||||||
|
через общий runner и выполняет только сравнение OpenAPI. Соседний frontend
|
||||||
|
checkout нужен для snapshot. Полные HTTP-сценарии запускает `test:e2e`;
|
||||||
|
миграция перед сравнением не подменяет проверку поведения приложения.
|
||||||
|
Подробности — в [сквозной проверке](verification.md#известное-ограничение-сквозной-проверки).
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# UI-каталог Storybook
|
||||||
|
|
||||||
|
Storybook 10.6.0 показывает реальные представления WealthLedger на общей
|
||||||
|
визуальной основе приложения без входа и подключения backend.
|
||||||
|
|
||||||
|
## Запуск и сборка
|
||||||
|
|
||||||
|
Каталог работает как отдельная локальная витрина по адресу
|
||||||
|
[localhost:6006](http://localhost:6006) либо как самостоятельная статическая
|
||||||
|
сборка. Его примеры не входят в пользовательские маршруты приложения.
|
||||||
|
Публикация отдельным сервисом не настроена.
|
||||||
|
|
||||||
|
## Компоненты и работа с примерами
|
||||||
|
|
||||||
|
Каталог включает 14 групп и 54 примера:
|
||||||
|
|
||||||
|
| Группа | Что показывает |
|
||||||
|
| --- | --- |
|
||||||
|
| Основы дизайн-системы | Палитры, семантические роли, типографику, интервалы, радиусы, тени и адаптивные размеры |
|
||||||
|
| Пустой результат и ошибки | Четыре варианта с действием и без |
|
||||||
|
| Загрузка | Доступную область занятости с нейтральными фигурами |
|
||||||
|
| Кнопки | Основное, вторичное и вспомогательное действие, выполнение и недоступность, работу с клавиатурой |
|
||||||
|
| Список счетов | Несколько счетов и валют, недоступную и пустую сводку, неоценённые бумаги, длинные названия и суммы |
|
||||||
|
| Карточка позиции | Акцию с положительным результатом, облигацию с отрицательным, прочий инструмент и отсутствие оценки |
|
||||||
|
| Ближайшие события | Успешный, загружаемый, ошибочный и пустой результат; неизвестную сумму и длинное название |
|
||||||
|
| Обновление портфеля | Сохранённые суммы при обновлении и ошибке, локальную повторную попытку |
|
||||||
|
| Загрузка страницы счёта | Контур ожидаемых заголовка, сводки и строк |
|
||||||
|
| Структура портфеля | Валюты и типы активов, только деньги, пустую и неполную оценку, неизвестные и отрицательные значения |
|
||||||
|
| Сводка банковских карт | Компактные расходы по категориям, отрицательный месяц возврата, известные нули, загрузку, ошибку, сохранённые суммы и длинные точные итоги |
|
||||||
|
| Внешний вид банковской карты | Цвета банков, маскированный номер, платёжные системы, отсутствующие реквизиты и длинные названия в архиве |
|
||||||
|
| Иконки банковских операций | Пиктограммы категорий расходов, общую иконку нестандартной категории и пополнение |
|
||||||
|
| Поля банковской карты | Независимую рублёвую карту и недоступные финансовые поля архива |
|
||||||
|
|
||||||
|
Каталог не перечисляет все элементы библиотеки или приложения. Отсутствие
|
||||||
|
представления в витрине не означает его отсутствия в продукте. Основы показывают
|
||||||
|
опубликованные шкалы и смысл ролей; особые размеры и тени конкретных экранов
|
||||||
|
не объявляются общими токенами. Границы описаны в [дизайн-системе](design-system.md).
|
||||||
|
|
||||||
|
Canvas показывает выбранный пример. Docs поясняет назначение и ограничения,
|
||||||
|
Controls меняет доступные свойства, Actions показывает локальные действия.
|
||||||
|
Наборы вымышленных данных и доступные Controls определяются конкретным
|
||||||
|
примером; подготовленные варианты перечислены в историях. Свойства можно сбросить, пример —
|
||||||
|
смонтировать заново; изменение размера просмотра не сбрасывает выбранные значения.
|
||||||
|
|
||||||
|
Витрина и приложение используют одну тему. Примеры показываются по-русски
|
||||||
|
в светлой схеме. Ошибка сохраняет доступное объявление, загрузка — состояние
|
||||||
|
занятости; декоративные фигуры не получают фокус.
|
||||||
|
|
||||||
|
## Границы и сопровождение
|
||||||
|
|
||||||
|
Все счета, позиции, события и суммы вымышлены. Каталог не читает сессии,
|
||||||
|
cookies, секреты и реальные пользовательские данные. Вместо внешних логотипов
|
||||||
|
показываются буквенные заглушки. Сама витрина может сохранять свои настройки;
|
||||||
|
телеметрия отключена.
|
||||||
|
|
||||||
|
Действия не обращаются к application API. Ссылки меняют только локальное
|
||||||
|
состояние примера, не открывают продуктовую страницу назначения и не меняют
|
||||||
|
адрес приложения. Повторная попытка демонстрирует локальное действие и не
|
||||||
|
доказывает восстановление реального запроса.
|
||||||
|
|
||||||
|
Сохранённые суммы иллюстрируют Client Cached Snapshot, но его жизненный цикл
|
||||||
|
здесь не исполняется. В приложении первичная загрузка показывает контур
|
||||||
|
ожидаемого содержимого, фоновое обновление сохраняет данные, а выполняемое
|
||||||
|
действие имеет контекстный индикатор. Подробности —
|
||||||
|
[состояния интерфейса](../architecture/cross-cutting/ui-resilience.md).
|
||||||
|
|
||||||
|
Автоматические проверки frontend охватывают каталог и его изоляцию от
|
||||||
|
продуктовых запросов. Примеры и их пояснения обновляются вместе с включёнными
|
||||||
|
представлениями. Клавиатура, реальные размеры и визуальное соответствие
|
||||||
|
проверяются в браузере; автоматической визуальной регрессии нет.
|
||||||
|
|
||||||
|
## Использование в UI задаче
|
||||||
|
|
||||||
|
Каталог помогает сравнить существующие состояния с ожидаемым пользовательским
|
||||||
|
сценарием. Он дополняет [принципы UI-решений](engineering-governance.md#ui-решения)
|
||||||
|
конкретными примерами, но успешная сборка не доказывает доступность,
|
||||||
|
визуальное соответствие и работу настоящего API.
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Что подтверждают проверки системы
|
||||||
|
|
||||||
|
Успешная сборка, запуск, равенство схем и прохождение пользовательского
|
||||||
|
сценария — разные доказательства. Выбор проверки зависит от наблюдаемого
|
||||||
|
риска: денежных вычислений, доступа, целостности данных или состояния интерфейса.
|
||||||
|
Общие основания описаны в [принципах сопровождения](engineering-governance.md#risk-based-testing).
|
||||||
|
|
||||||
|
## Интерфейс
|
||||||
|
|
||||||
|
Локальный gate frontend охватывает типы, статический анализ, автоматические
|
||||||
|
сценарии, зависимости и сборки приложения и UI-каталога. Он выполняется
|
||||||
|
без запущенного backend. Покрытие ограничивает регрессии, но не доказывает
|
||||||
|
правильность финансового расчёта, доступность и полный пользовательский сценарий.
|
||||||
|
|
||||||
|
[Каталог](ui-catalog.md) показывает существующие визуальные решения на
|
||||||
|
вымышленных данных. Широкий и узкий экран, клавиатурная навигация,
|
||||||
|
восстановление после ошибок и соответствие задаче проверяются в интерфейсе
|
||||||
|
отдельно от сборки. Изолированная тестовая среда не воспроизводит все свойства
|
||||||
|
браузера и живой интеграции.
|
||||||
|
|
||||||
|
## API и хранение данных
|
||||||
|
|
||||||
|
Локальный gate backend объединяет статический анализ, типы приложения и тестов,
|
||||||
|
автоматические сценарии и сборку. Он не исправляет исходные файлы;
|
||||||
|
автоматическое форматирование выполняется отдельно и не является доказательством.
|
||||||
|
Диагностика покрытия доступна без общего числового порога.
|
||||||
|
|
||||||
|
Равенство OpenAPI подтверждает согласованность публичного описания с клиентом.
|
||||||
|
Валидация, cookie, авторизация счёта, статусы и ошибки требуют полного HTTP-потока;
|
||||||
|
целостность и конкурентные изменения данных — проверки с изолированной реальной
|
||||||
|
базой. Подставной ответ базы не доказывает её транзакционные свойства.
|
||||||
|
Направление генерации и границы сравнения — в [проверке OpenAPI](runtime-openapi-verification.md).
|
||||||
|
|
||||||
|
### Известное ограничение сквозной проверки
|
||||||
|
|
||||||
|
Прежний blocker WL-P2-05 снят в конфигурации Prisma: на Node 22.21.0 /
|
||||||
|
Prisma 7.9.1 унаследованный `RUST_LOG=warn` или `error` воспроизводит пустой
|
||||||
|
`Schema engine error`, а отсутствие переменной позволяет fresh migration.
|
||||||
|
Backend Prisma config удаляет переменную в процессе CLI после загрузки `.env`;
|
||||||
|
это не меняет application logging. Версии зависимостей и SQL history сохранены.
|
||||||
|
|
||||||
|
`npm run test:e2e` применяет migrations и запускает каждую HTTP suite на отдельной
|
||||||
|
временной DB. Один URL передаётся Prisma и Jest; прямой запуск Jest без runner
|
||||||
|
отклоняется до DB-запросов. При ошибке и SIGINT/SIGTERM выполняется cleanup.
|
||||||
|
`npm run test:sqlite` выбирает auth/cards; `test:durability` отдельно проверяет
|
||||||
|
migration, restart, возврат предыдущего приложения и backup/restore.
|
||||||
|
Команды и runnable regression — в
|
||||||
|
[backend README](../../backend/README.md#реальная-sqlite-и-сохранность-данных),
|
||||||
|
фактические результаты — в
|
||||||
|
[registry](audit-remediation.md#2026-09-13--wl-p2-05-воспроизводимый-sqlite-e2e).
|
||||||
|
|
||||||
|
Backend `verify` остаётся статическим/unit/build gate; полный HTTP/DB scope
|
||||||
|
проверяется отдельным `test:e2e`. Успех этих команд не подтверждает доступность
|
||||||
|
внешнего T-Bank. HTTP tests требуют локальных портов, которые sandbox может
|
||||||
|
запрещать. Безопасное сохранение реальных данных — в
|
||||||
|
[локальном окружении](local-workspace.md).
|
||||||
|
|
||||||
|
## Где выполнять проверки
|
||||||
|
|
||||||
|
Команды и конкретные тестовые правила поддерживаются в локальных инструкциях
|
||||||
|
[frontend](../../frontend/AGENTS.md) и [backend](../../backend/AGENTS.md).
|
||||||
|
Проверки этой документации — в [CONTRIBUTING](../CONTRIBUTING.md#проверки).
|
||||||
|
Отчёт различает пройденную границу, оставшееся ограничение и проверяемое состояние;
|
||||||
|
успех одного gate не распространяется автоматически на весь workspace.
|
||||||
@@ -7,11 +7,30 @@
|
|||||||
только к backend. Счёт не хранится как долговременное локальное владение
|
только к backend. Счёт не хранится как долговременное локальное владение
|
||||||
независимо от брокерского подключения.
|
независимо от брокерского подключения.
|
||||||
|
|
||||||
Экран читает локальный список и сохранённые сводки. Обычный запрос запускает
|
```mermaid
|
||||||
недостающие или устаревшие зависимости в фоне. Список и сводка каждого счёта
|
sequenceDiagram
|
||||||
имеют независимые timestamps и состояния. Неподтверждённый список показывает
|
participant U as Пользователь
|
||||||
загрузку доступа, а не отсутствие счетов. Поток и границы описаны в
|
participant F as Frontend
|
||||||
[модели свежести](../../architecture/cross-cutting/data-freshness.md).
|
participant B as Backend
|
||||||
|
participant D as SQLite
|
||||||
|
participant T as T-Bank gRPC
|
||||||
|
U->>F: Открывает /accounts
|
||||||
|
F->>B: GET /api/v1/brokerage-accounts (Bearer)
|
||||||
|
B->>D: Читает подключение T-Bank
|
||||||
|
alt Подключение отсутствует
|
||||||
|
B-->>F: 409 TBANK_NOT_CONNECTED
|
||||||
|
F-->>U: Переход к настройкам T-Bank
|
||||||
|
else Подключение сохранено
|
||||||
|
B->>T: GetAccounts(OPEN) over TLS
|
||||||
|
T-->>B: Счета или ошибка
|
||||||
|
par Для каждого поддерживаемого счета
|
||||||
|
B->>T: GetPortfolio + GetPositions over TLS
|
||||||
|
T-->>B: Позиции и деньги либо временная ошибка
|
||||||
|
end
|
||||||
|
B-->>F: Счета со сводками либо ошибка
|
||||||
|
F-->>U: Карточки счетов или объяснение ошибки
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
## Счёт и его сводка
|
## Счёт и его сводка
|
||||||
|
|
||||||
@@ -50,8 +69,8 @@
|
|||||||
сводка не подменяется нулями.
|
сводка не подменяется нулями.
|
||||||
|
|
||||||
Временная недоступность сводки одного счёта остаётся локальной для него.
|
Временная недоступность сводки одного счёта остаётся локальной для него.
|
||||||
Известный отказ доступа скрывает личные данные. Transient failure сохраняет
|
Ошибка подключения, отклонение токена или невозможность получить исходный
|
||||||
последний список и его время; отсутствие первого списка остаётся missing.
|
список не превращаются в частичный успешный список.
|
||||||
|
|
||||||
## Состояния
|
## Состояния
|
||||||
|
|
||||||
@@ -62,22 +81,20 @@
|
|||||||
|
|
||||||
- `409 TBANK_NOT_CONNECTED`: список не запрашивается у T-Bank, экран ведёт в настройки.
|
- `409 TBANK_NOT_CONNECTED`: список не запрашивается у T-Bank, экран ведёт в настройки.
|
||||||
- `400 INVALID_TBANK_TOKEN`: сохранённый токен не удаляется, экран ведёт в настройки для замены или удаления.
|
- `400 INVALID_TBANK_TOKEN`: сохранённый токен не удаляется, экран ведёт в настройки для замены или удаления.
|
||||||
- `503 TBANK_UNAVAILABLE`: экран показывает спокойное состояние недоступности; чтение возобновляется автоматически.
|
- `503 TBANK_UNAVAILABLE`: экран предлагает повторить загрузку или открыть настройки.
|
||||||
- Неизвестная ошибка: то же состояние недоступности без действия повтора.
|
- Неизвестная ошибка: экран предлагает только повторить загрузку.
|
||||||
- Успешный пустой ответ: штатное состояние отсутствия инвестиционных счетов.
|
- Успешный пустой ответ: штатное состояние отсутствия инвестиционных счетов.
|
||||||
|
|
||||||
Карточка ведёт на `/accounts/:accountId` по всей области и доступна с клавиатуры. Карточки расположены в одну колонку при любой ширине. Карточка показывает название, локализованный тип, крупный валютный итог и вторую валюту при наличии, число остальных валют, дату открытия без времени и видимое действие открытия. Отдельные цветные блоки «Акции» и «Облигации» показывают количество наименований бумаг. Недоступная или пустая сводка отображается локально; отсутствие сумм не скрывает известные количества бумаг, а недоступная сводка не подменяет их нулями. Наведение и видимый клавиатурный фокус подчёркивают переход; настройка уменьшения движения отключает анимацию.
|
Карточка ведёт на `/accounts/:accountId` по всей области и доступна с клавиатуры. На широком экране карточки расположены в две колонки, на узком — в одну. Карточка показывает название, локализованный тип, крупный валютный итог и вторую валюту при наличии, число остальных валют, дату открытия без времени и видимое действие открытия. Отдельные цветные блоки «Акции» и «Облигации» показывают количество наименований бумаг. Недоступная или пустая сводка отображается локально; отсутствие сумм не скрывает известные количества бумаг, а недоступная сводка не подменяет их нулями. Наведение и видимый клавиатурный фокус подчёркивают переход; настройка уменьшения движения отключает анимацию.
|
||||||
|
|
||||||
## Свежесть и границы
|
## Свежесть и границы
|
||||||
|
|
||||||
Обычное открытие использует query owner и локальный server snapshot.
|
Список запрашивается при открытии экрана без ручного обновления и без обновления при возврате фокуса. Frontend использует успешный кэш TanStack Query в течение 60 секунд, поэтому повторный вход в этот интервал не выполняет новый GET. Когда уже показанный список обновляется в фоне, он остаётся на экране: индикатор обновления и ошибка с повторной попыткой отображаются рядом с последним успешным ответом, включая пустой список. Это клиентский сохранённый ответ; серверный запасной список не выдаётся. Backend не хранит кэш и не передаёт метку свежести. Список не заменяет полный [портфель](../portfolio/overview.md),
|
||||||
«Обновить» перечитывает сохранённые membership и сводки доступных счетов;
|
[журнал операций](../operations/overview.md) или историю закрытых счетов.
|
||||||
активная работа наблюдается без нового импорта. Истечение TTL само не
|
Для совокупного результата доступен [режим «Все счета»](../portfolio/all-accounts.md).
|
||||||
запускает запрос: обновлением личных данных владеет часовой обход.
|
|
||||||
Membership и portfolio source имеют часовую свежесть. Время списка
|
|
||||||
не подменяет время каждой сводки. Список не заменяет полный
|
|
||||||
[портфель](../portfolio/overview.md), [журнал](../operations/overview.md)
|
|
||||||
или историю закрытых счетов. Для общего результата доступен
|
|
||||||
[режим «Все счета»](../portfolio/all-accounts.md).
|
|
||||||
|
|
||||||
Согласованное поведение описывает [спецификация счетов](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/brokerage-accounts/spec.md).
|
Согласованное поведение описывает [спецификация счетов](../../../specs/openspec/specs/brokerage-accounts/spec.md).
|
||||||
|
Её раздел об экране пока требует одноколоночного списка и не описывает
|
||||||
|
счётчики акций и облигаций. Текущая реализация и её проверки используют
|
||||||
|
две колонки на широком экране и показывают эти счётчики. Расхождение требует
|
||||||
|
отдельного согласования требований; в этой документации отражён наблюдаемый экран.
|
||||||
|
|||||||
@@ -11,25 +11,19 @@
|
|||||||
|
|
||||||
Пользователь регистрируется или входит по email и паролю. Регистрация сразу
|
Пользователь регистрируется или входит по email и паролю. Регистрация сразу
|
||||||
создаёт первую сессию. Внешние пробелы email удаляются, буквы приводятся
|
создаёт первую сессию. Внешние пробелы email удаляются, буквы приводятся
|
||||||
к нижнему регистру.
|
к нижнему регистру. Backend требует пароль не короче 12 символов и не длиннее
|
||||||
|
72 UTF-8 байтов.
|
||||||
|
|
||||||
Пароль при регистрации — от 8 до 72 печатных ASCII-символов: латинские
|
**Известное расхождение:** сохранённое требование регистрации и проверка
|
||||||
буквы, цифры, спецсимволы и пробел. Кириллица, emoji, другие не-ASCII и
|
в форме frontend считают нижнюю границу в UTF-8 байтах, а backend — в символах.
|
||||||
управляющие символы отклоняются. Пароль не обрезается и не нормализуется, а
|
Не-ASCII пароль может пройти форму и получить отказ API. Это не принятое
|
||||||
обязательных классов символов нет. Верхняя граница вытекает из bcrypt, который
|
изменение password policy; требуется отдельное согласованное решение
|
||||||
учитывает только первые 72 байта: для ASCII символ равен байту, поэтому
|
о проверке пароля и подтверждение полного HTTP-потока.
|
||||||
длинный пароль не обрезается молча. Frontend и backend проверяют одну и ту же
|
|
||||||
политику.
|
|
||||||
|
|
||||||
При входе проверяется только то, что пароль непустой и не длиннее 72 UTF-8
|
|
||||||
байтов. Минимум и набор символов не применяются, чтобы пользователь,
|
|
||||||
зарегистрированный по прежним правилам (в том числе с не-ASCII паролем), мог
|
|
||||||
войти. Невалидный пароль получает `400` с кодом `VALIDATION_ERROR`.
|
|
||||||
|
|
||||||
Access token действует 15 минут и хранится только в памяти браузера.
|
Access token действует 15 минут и хранится только в памяти браузера.
|
||||||
Refresh token имеет серверный срок 7 дней, заменяется при восстановлении
|
Refresh token имеет серверный срок 7 дней, заменяется при восстановлении
|
||||||
сессии и передаётся в cookie `refresh_token` с `httpOnly` и `SameSite=Lax`.
|
сессии и передаётся в cookie `refresh_token` с `httpOnly` и `SameSite=Lax`.
|
||||||
Сроки определены в [спецификации сессий](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/user-authentication/spec.md#requirement-access-и-refresh-сессии)
|
Сроки определены в [спецификации сессий](../../../specs/openspec/specs/user-authentication/spec.md#requirement-access-и-refresh-сессии)
|
||||||
и [ADR 006](../../adr/006-auth-session-policy.md).
|
и [ADR 006](../../adr/006-auth-session-policy.md).
|
||||||
|
|
||||||
После перезагрузки SPA получает новый access token через refresh-cookie.
|
После перезагрузки SPA получает новый access token через refresh-cookie.
|
||||||
@@ -61,16 +55,15 @@ sequenceDiagram
|
|||||||
B-->>F: Access token, профиль, новая refresh-cookie
|
B-->>F: Access token, профиль, новая refresh-cookie
|
||||||
F->>B: Protected request с Bearer
|
F->>B: Protected request с Bearer
|
||||||
else Cookie нет или refresh отклонён
|
else Cookie нет или refresh отклонён
|
||||||
B-->>F: 401 без изменения cookie
|
B-->>F: 401 и очистка cookie
|
||||||
Note over F: Готовое неавторизованное состояние
|
Note over F: Готовое неавторизованное состояние
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
В одной вкладке одновременные refresh используют один незавершённый запрос;
|
В одном экземпляре frontend одновременные refresh используют один
|
||||||
cookie-writing requests упорядочены локально. Межвкладочная синхронизация
|
незавершённый запрос. Между вкладками используется Web Locks, если браузер
|
||||||
не поддерживается: после смены сессии в другой вкладке допустимы reload/re-login.
|
его поддерживает. Backend всё равно защищает ротацию атомарно: координация
|
||||||
Backend защищает ротацию атомарно. Неуспешный refresh не удаляет cookie
|
браузеров не заменяет серверную проверку.
|
||||||
победившего запроса; недействительный token по-прежнему не восстанавливает сессию.
|
|
||||||
|
|
||||||
Обычный защищённый запрос с `401 UNAUTHORIZED` проходит refresh и один повтор
|
Обычный защищённый запрос с `401 UNAUTHORIZED` проходит refresh и один повтор
|
||||||
через общий транспорт; auth endpoints из этого цикла исключены. Само
|
через общий транспорт; auth endpoints из этого цикла исключены. Само
|
||||||
@@ -79,11 +72,11 @@ Backend защищает ротацию атомарно. Неуспешный r
|
|||||||
## HTTP-граница
|
## HTTP-граница
|
||||||
|
|
||||||
Endpoints, статусы и формы публичных ответов определяет backend OpenAPI;
|
Endpoints, статусы и формы публичных ответов определяет backend OpenAPI;
|
||||||
граница производного клиента описана в
|
согласование производного клиента описано в
|
||||||
[HTTP API](../../architecture/http-api.md#источник-контракта).
|
[contract workflow](../../development/runtime-openapi-verification.md).
|
||||||
Refresh-cookie ограничена auth API. Признак Secure зависит от режима
|
Refresh-cookie ограничена auth API. Признак Secure зависит от режима
|
||||||
окружения: в development допускается локальный HTTP, в остальных режимах
|
окружения: локальный HTTP предполагает development mode, см.
|
||||||
Secure-cookie требует защищённого соединения.
|
[локальное окружение](../../development/local-workspace.md).
|
||||||
|
|
||||||
HTTP-ошибка и транспортная ошибка refresh одинаково завершают начальное
|
HTTP-ошибка и транспортная ошибка refresh одинаково завершают начальное
|
||||||
восстановление готовым неавторизованным состоянием. При refresh после
|
восстановление готовым неавторизованным состоянием. При refresh после
|
||||||
@@ -124,6 +117,6 @@ Backend проверяет учётные данные и tokens, хранит
|
|||||||
и проверку атомарности с реальной изолированной базой.
|
и проверку атомарности с реальной изолированной базой.
|
||||||
|
|
||||||
Согласованное поведение находится в workspace-спецификации
|
Согласованное поведение находится в workspace-спецификации
|
||||||
[User authentication](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/user-authentication/spec.md).
|
[User authentication](../../../specs/openspec/specs/user-authentication/spec.md).
|
||||||
Правила начального ожидания — в
|
Правила начального ожидания — в
|
||||||
[состояниях интерфейса](../../architecture/cross-cutting/ui-resilience.md).
|
[состояниях интерфейса](../../architecture/cross-cutting/ui-resilience.md).
|
||||||
|
|||||||
@@ -89,8 +89,9 @@ JavaScript Number. Отдельный ввод ограничен signed64: на
|
|||||||
следующие страницы. Баланс при выборе прошлого периода остаётся текущим.
|
следующие страницы. Баланс при выборе прошлого периода остаётся текущим.
|
||||||
Сводка учитывает все подходящие записи, включая не показанные на странице.
|
Сводка учитывает все подходящие записи, включая не показанные на странице.
|
||||||
|
|
||||||
Сводка одной или всех карт отдельно показывает расходы, возвращённую сумму и
|
Сводка одной или всех карт показывает одну итоговую сумму расходов с учётом
|
||||||
итог после возвратов, а также компактные строки категорий по убыванию итоговых расходов. Возврат уменьшает расходы месяца своего
|
возвратов и компактные строки категорий по убыванию итоговых расходов. Отдельные показатели «Возвращено»
|
||||||
|
и «После возвратов» в интерфейсе скрыты; расчёт и операции возврата сохранены. Возврат уменьшает расходы месяца своего
|
||||||
получения: августовская покупка 1 000 ₽ и сентябрьский возврат 600 ₽ дают
|
получения: августовская покупка 1 000 ₽ и сентябрьский возврат 600 ₽ дают
|
||||||
августовский итог 1 000 ₽ и сентябрьский −600 ₽ при отсутствии других трат.
|
августовский итог 1 000 ₽ и сентябрьский −600 ₽ при отсутствии других трат.
|
||||||
Начальный остаток, пополнения и переводы в эти показатели не входят.
|
Начальный остаток, пополнения и переводы в эти показатели не входят.
|
||||||
@@ -135,10 +136,10 @@ JavaScript Number. Отдельный ввод ограничен signed64: на
|
|||||||
а форма сохраняет черновик и позволяет получить актуальную запись.
|
а форма сохраняет черновик и позволяет получить актуальную запись.
|
||||||
При ошибке сохранения неподтверждённые суммы не выдаются за новый баланс.
|
При ошибке сохранения неподтверждённые суммы не выдаются за новый баланс.
|
||||||
|
|
||||||
[Безопасное восстановление](../../architecture/containers.md#сохранение-и-восстановление-данных)
|
[Безопасное восстановление](../../development/local-workspace.md#сохранение-данных-и-откат)
|
||||||
сохраняет новую финансовую историю и при откате приложения. Устройство хранения
|
сохраняет новую финансовую историю и при откате приложения. Устройство хранения
|
||||||
описано в [контейнерах](../../architecture/containers.md#хранимые-данные-и-границы-доступа).
|
описано в [контейнерах](../../architecture/containers.md#хранимые-данные-и-границы-доступа).
|
||||||
Согласованный контракт банковского учёта находится в
|
Согласованный контракт этой поставки находится в
|
||||||
[manual-bank-cards](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/changes/add-manual-bank-cards/specs/manual-bank-cards/spec.md).
|
[manual-bank-cards](../../../specs/openspec/changes/add-manual-bank-cards/specs/manual-bank-cards/spec.md).
|
||||||
Автоматическая загрузка банковских данных, кредитные карты, другие валюты,
|
Автоматическая загрузка банковских данных, кредитные карты, другие валюты,
|
||||||
бюджеты и общий баланс банковских денег с инвестициями в эту версию не входят.
|
бюджеты и общий баланс банковских денег с инвестициями в эту версию не входят.
|
||||||
|
|||||||
+58
-33
@@ -4,7 +4,7 @@
|
|||||||
купонами и дивидендами за выбранный период». Пользователь открывает
|
купонами и дивидендами за выбранный период». Пользователь открывает
|
||||||
`/accounts/:accountId/income` из обзора счёта или
|
`/accounts/:accountId/income` из обзора счёта или
|
||||||
[общий доход](../portfolio/all-accounts.md) по доступным счетам.
|
[общий доход](../portfolio/all-accounts.md) по доступным счетам.
|
||||||
Экран показывает итоги по валютам и отдельную карточку каждой
|
Экран показывает итоги по валютам, сравнение и отдельную карточку каждой
|
||||||
включённой выплаты. Источником служат [исполненные операции](../operations/overview.md),
|
включённой выплаты. Источником служат [исполненные операции](../operations/overview.md),
|
||||||
а не изменение цены портфеля и не календарь будущих выплат.
|
а не изменение цены портфеля и не календарь будущих выплат.
|
||||||
|
|
||||||
@@ -38,36 +38,44 @@
|
|||||||
|
|
||||||
При первой загрузке или новом периоде без сохранённых данных показан
|
При первой загрузке или новом периоде без сохранённых данных показан
|
||||||
каркас страницы. Пустой результат объясняет отсутствие исполненных купонов и
|
каркас страницы. Пустой результат объясняет отсутствие исполненных купонов и
|
||||||
дивидендов, сохраняя фильтры и налоговое пояснение. Ошибка запроса
|
дивидендов, сохраняя фильтры, налоговое пояснение и сравнения. Ошибка запроса
|
||||||
или нарушение принадлежности выплат даёт локальное состояние недоступности
|
или нарушение принадлежности выплат даёт локальное состояние с действием «Повторить».
|
||||||
без действия повтора; чтение восстанавливается автоматически.
|
|
||||||
При фоновом обновлении того же периода сохранённый ответ и черновик
|
При фоновом обновлении того же периода сохранённый ответ и черновик
|
||||||
остаются видимыми без индикации обновления и технического предупреждения.
|
остаются видимыми: загрузку обозначает индикатор, неуспешное обновление —
|
||||||
Неполнота применённого периода обозначается состоянием самого результата.
|
предупреждение с повторной попыткой.
|
||||||
|
|
||||||
На узком экране поля и действия идут вертикально; все карточки выплат
|
На узком экране поля и действия идут вертикально; все карточки выплат
|
||||||
остаются в общем потоке страницы без раскрытия или внутренних областей прокрутки.
|
остаются в общем потоке страницы без раскрытия или внутренних областей прокрутки.
|
||||||
|
|
||||||
## Какие выплаты входят в результат
|
## Какие выплаты входят в результат
|
||||||
|
|
||||||
- Backend проверяет доступ локально и читает сохранённые операции. Фоновая загрузка должна завершить все страницы; ошибка страницы или повреждённая цепочка курсоров не подтверждают полноту.
|
- Backend проверяет доступ к счёту, проходит все страницы исполненных операций и не возвращает частичный результат при ошибке страницы или повреждённой цепочке курсоров.
|
||||||
- Только точные типы купона и дивиденда источника формируют результат. Суммы остаются точными десятичными строками в исходной валюте; отрицательные корректировки сохраняют знак. Налоги, комиссии, сделки, переводы, погашения и переводы дивидендов не входят в доход.
|
- Только точные типы купона и дивиденда источника формируют результат. Суммы остаются точными десятичными строками в исходной валюте; отрицательные корректировки сохраняют знак. Налоги, комиссии, сделки, переводы, погашения и переводы дивидендов не входят в доход.
|
||||||
- Итоги, временные интервалы и разбиение по инструментам не смешивают валюты и не создают общий конвертированный итог. Система не загружает другие периоды ради сравнения полученного дохода с выбранным.
|
- Итоги, сравнения, временные интервалы и разбиение по инструментам не смешивают валюты и не создают общий конвертированный итог. На странице показано только сравнение с годом ранее, когда календарная дата и история счёта позволяют. Предыдущий период остаётся в ответе API, но не отображается.
|
||||||
- Временные интервалы по-прежнему приходят с backend: дни для 1–45 дней, недели для 46–180 и месяцы для более длинного периода, включая пустые интервалы. Страница дохода не строит из них график или текстовую динамику; ответ API не меняется.
|
- Временные интервалы по-прежнему приходят с backend: дни для 1–45 дней, недели для 46–180 и месяцы для более длинного периода, включая пустые интервалы. Страница дохода не строит из них график или текстовую динамику; ответ API не меняется.
|
||||||
- Раздел «Выплаты» показывает единый список карточек выплат в исходном порядке `entries`: одна карточка на каждую выплату. Внешних групп по инструментам или валютам, итогов и количества выплат инструмента нет. Описание, дата, доступные идентификаторы инструмента и сумма со знаком видны в каждой карточке; валюты разделены в итогах. Дополнительный запрос за составом не нужен.
|
- Раздел «Выплаты» показывает единый список карточек выплат в исходном порядке `entries`: одна карточка на каждую выплату. Внешних групп по инструментам или валютам, итогов и количества выплат инструмента нет. Описание, дата, доступные идентификаторы инструмента и сумма со знаком видны в каждой карточке; валюты по-прежнему разделены в итогах и сравнениях. Дополнительный запрос за составом не нужен.
|
||||||
|
|
||||||
|
Сравнение с непосредственно предыдущим периодом сохраняется в API:
|
||||||
|
для полного месяца это предыдущий полный месяц, иначе — непосредственно
|
||||||
|
предшествующий интервал той же включительной длительности. Текущая страница
|
||||||
|
его не показывает. Это расходится с
|
||||||
|
[основным требованием сравнений](../../../specs/openspec/specs/brokerage-account-income-analytics/spec.md#requirement-period-comparisons-preserve-calendar-and-currency-meaning),
|
||||||
|
которое описывает оба сравнения. Требуется отдельно согласовать этот разрыв;
|
||||||
|
здесь сохранено различие между ответом API и фактическим экраном.
|
||||||
|
|
||||||
|
Раздел «Годом ранее» показывает цветные панели без общей белой подложки и
|
||||||
|
внутренних отступов у внешнего блока. Точные периоды показаны в заголовках двух панелей с русским форматом диапазона
|
||||||
|
и UTC для календарных дат.
|
||||||
|
Отдельного заголовка с датами и валютного бейджа нет; валюта остаётся у каждой суммы.
|
||||||
|
Панель «Разница» сохраняет серверную абсолютную разницу и дополняет итог, купоны
|
||||||
|
и дивиденды процентом: абсолютная разница / сумма годом ранее × 100.
|
||||||
|
Это округлённый показатель для отображения, а не пересчёт Money или инвестиционная
|
||||||
|
доходность. Форматирование сохраняет знак и не более одного десятичного разряда.
|
||||||
|
При нулевой или отрицательной базе либо непредставимом числовом отношении
|
||||||
|
процентный бейдж не отображается; абсолютная разница остаётся видимой.
|
||||||
|
|
||||||
## Полнота и принадлежность выплат
|
## Полнота и принадлежность выплат
|
||||||
|
|
||||||
Доходы читают сохранённые операции и coverage в согласованной транзакции.
|
|
||||||
Только выбранный период определяет готовность результата. Неполный период
|
|
||||||
не получает окончательных итогов.
|
|
||||||
Подтверждённый пустой период отличается от ещё не загруженного. При смене
|
|
||||||
периода прежний snapshot не показывается под новым названием.
|
|
||||||
|
|
||||||
Ручная сверка полного периода сохраняет весь прежний результат до успешной
|
|
||||||
атомарной публикации всех необходимых интервалов и счетов. История счёта
|
|
||||||
может ещё догружаться после готовности выбранного месяца.
|
|
||||||
|
|
||||||
Каждая запись `income-analytics.entries` содержит обязательный `instrumentKey`,
|
Каждая запись `income-analytics.entries` содержит обязательный `instrumentKey`,
|
||||||
равный `key` соответствующей строки `instruments`. Backend вычисляет этот ключ
|
равный `key` соответствующей строки `instruments`. Backend вычисляет этот ключ
|
||||||
один раз при подготовке записей дохода и использует его же для группировки.
|
один раз при подготовке записей дохода и использует его же для группировки.
|
||||||
@@ -75,28 +83,45 @@
|
|||||||
серверным правилом; клиент воспринимает ключ как непрозрачную строку.
|
серверным правилом; клиент воспринимает ключ как непрозрачную строку.
|
||||||
Это поле принадлежит ответу аналитики; общий журнал операций его не требует.
|
Это поле принадлежит ответу аналитики; общий журнал операций его не требует.
|
||||||
|
|
||||||
Backend обеспечивает принадлежность выплат, валюту и receipt count при
|
Frontend проверяет принадлежность выплат по полученному ключу до подготовки
|
||||||
построении entries и instrument groups из одного набора данных. Frontend
|
плоского списка выплат. Отсутствующий, пустой или
|
||||||
доверяет типизированному contract, не восстанавливает формулу идентификации
|
нестроковый ключ, неоднозначные ключи инструментов, неизвестная группа, другая
|
||||||
и не выполняет второй consistency validator. Полностью пустой ответ допустим.
|
валюта или несовпадение числа выплат приводят к существующему локальному
|
||||||
Состав и исходный серверный порядок записей и знак сумм
|
состоянию ошибки аналитики с повторной попыткой. Частичный список выплат не показывается как
|
||||||
сохраняются, frontend не пересчитывает денежные итоги. Стабильный ID в пределах подключения и счёта определяет одну операцию:
|
полный; формула идентификации в клиенте не восстанавливается. Полностью пустой
|
||||||
повторная сверка обновляет её без двойного учёта. Одинаковые ID разных счетов
|
ответ допустим. Состав и исходный серверный порядок записей и знак сумм
|
||||||
остаются разными выплатами. Изменение источника логотипа позволяет заново
|
сохраняются, frontend не пересчитывает денежные итоги. Повторяющиеся ID
|
||||||
показать изображение после буквенной заглушки.
|
операций остаются отдельными строками; повторные вхождения учитываются раздельно, а изменение источника логотипа
|
||||||
|
позволяет заново показать изображение после буквенной заглушки.
|
||||||
|
|
||||||
|
## Совместимость ответа
|
||||||
|
|
||||||
|
При обновлении с версии без `instrumentKey` сначала обновляется backend:
|
||||||
|
дополнительное поле совместимо с прежним
|
||||||
|
frontend. Затем синхронизируются OpenAPI snapshot/generated types и обновляется
|
||||||
|
клиент. При переключении клиент перезагружается, чтобы не переносить
|
||||||
|
старые результаты в памяти и незавершённые запросы. Новый frontend требует новый
|
||||||
|
backend; отсутствие ключа принадлежности не скрывается подстановкой прежней формулы.
|
||||||
|
|
||||||
|
При регрессии сначала возвращается прежний frontend вместе с соответствующим
|
||||||
|
snapshot/types и перезагружается клиент; затем при необходимости возвращается
|
||||||
|
backend. Старый frontend может временно работать с новым ответом с добавленным полем.
|
||||||
|
Календарные фильтры и отдельные карточки выплат используют тот же контракт и не требуют
|
||||||
|
дополнительного обновления backend. Схема SQLite, учётные данные и сообщённые брокером
|
||||||
|
операции не меняются: откат
|
||||||
|
не удаляет данные, производные результаты запрашиваются заново.
|
||||||
|
|
||||||
## Будущие события и ограничения
|
## Будущие события и ограничения
|
||||||
|
|
||||||
На странице дохода нет блоков «Динамика» и «Ближайший прогноз», блоков
|
На странице дохода нет блоков «Динамика» и «Ближайший прогноз». Она не запрашивает
|
||||||
сравнения периодов и сообщений об их недоступности. Она не запрашивает
|
|
||||||
`/events` и не зависит от его доступности. Самостоятельная страница
|
`/events` и не зависит от его доступности. Самостоятельная страница
|
||||||
[«Будущие события»](upcoming-events.md) по-прежнему доступна из обзора
|
[«Будущие события»](upcoming-events.md) по-прежнему доступна из обзора
|
||||||
выбранного счёта и сохраняет прежнее поведение. Будущие выплаты
|
выбранного счёта и сохраняет прежнее поведение. Будущие выплаты
|
||||||
не включаются в итоги полученного дохода.
|
не включаются в итоги полученного дохода или сравнения.
|
||||||
|
|
||||||
Аналитика читает сохранённую историю операций и не рассчитывает налоги, инвестиционную эффективность, доходность, валютную конвертацию или общий мультивалютный итог.
|
Аналитика только читает данные, не хранит историю операций и не рассчитывает налоги, инвестиционную эффективность, доходность, валютную конвертацию или общий мультивалютный итог.
|
||||||
|
|
||||||
Основное проверяемое поведение — в [спецификации дохода](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/brokerage-account-income-analytics/spec.md),
|
Основное проверяемое поведение — в [спецификации дохода](../../../specs/openspec/specs/brokerage-account-income-analytics/spec.md),
|
||||||
точная форма ответа — в [HTTP API](../../architecture/http-api.md).
|
точная форма ответа — в [HTTP API](../../architecture/http-api.md).
|
||||||
Не следует использовать календарь будущих событий для восстановления
|
Не следует использовать календарь будущих событий для восстановления
|
||||||
состава уже полученного дохода.
|
состава уже полученного дохода.
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
Суммы рассчитаны по количеству на `calculatedAt`. Они не учитывают будущие
|
Суммы рассчитаны по количеству на `calculatedAt`. Они не учитывают будущие
|
||||||
сделки, право владения на дату реестра и персональный налоговый расчёт.
|
сделки, право владения на дату реестра и персональный налоговый расчёт.
|
||||||
Есть неразрешённое ограничение интерпретации:
|
Есть неразрешённое ограничение интерпретации:
|
||||||
[основная спецификация](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/brokerage-account-upcoming-events/spec.md#requirement-forecasts-preserve-event-meaning-and-uncertainty)
|
[основная спецификация](../../../specs/openspec/specs/brokerage-account-upcoming-events/spec.md#requirement-forecasts-preserve-event-meaning-and-uncertainty)
|
||||||
и текущая подсказка экрана называют прогноз суммой «до налогов», но для
|
и текущая подсказка экрана называют прогноз суммой «до налогов», но для
|
||||||
дивиденда реализация только умножает `dividend_net` на количество.
|
дивиденда реализация только умножает `dividend_net` на количество.
|
||||||
Налоговый смысл этого поля не подтверждён локальными материалами; на
|
Налоговый смысл этого поля не подтверждён локальными материалами; на
|
||||||
@@ -39,67 +39,66 @@
|
|||||||
|
|
||||||
## Период и список
|
## Период и список
|
||||||
|
|
||||||
Без фильтра страница применяет период по умолчанию из семи календарных
|
Без фильтра период начинается сегодня в UTC и заканчивается через 30 дней
|
||||||
дат включительно: сегодняшней и шести следующих календарных дней. API при
|
после начала, обе даты включены. Поля «С» и «По» позволяют изменить период:
|
||||||
отсутствии параметров использует своё окно (от текущей даты UTC до `from + 30`
|
«Применить» сохраняет выбранные календарные даты в URL и запрашивает результат,
|
||||||
календарных дней), обе даты включены; `from` и `to` принимают только строгие
|
«Сбросить» возвращает период по умолчанию. Обзор счёта сохраняет прогноз
|
||||||
даты `YYYY-MM-DD`. Календарь фильтра разрешает выбор сегодняшней и будущих дат
|
на текущую дату и следующие 30 дней.
|
||||||
и не позволяет выбрать даты раньше сегодняшней. Прямая ссылка с валидным
|
|
||||||
прошедшим периодом сохраняет указанные даты без замены; некорректный формат,
|
|
||||||
несуществующая календарная дата или обратный диапазон нормализуются к периоду
|
|
||||||
по умолчанию с удалением параметров из URL. Поля «С» и «По» сохраняют
|
|
||||||
выбранные календарные даты в URL, а «Сбросить» возвращает период по умолчанию.
|
|
||||||
|
|
||||||
События упорядочены по календарной дате. Купоны и дивиденды показываются
|
События упорядочены по календарной дате. У каждой строки видны тип,
|
||||||
единым предметным представлением выплаты с явной пометкой прогноза — тем же,
|
|
||||||
что у исполненных выплат в доходе и журнале; неизвестная сумма остаётся
|
|
||||||
«сумма неизвестна» и не превращается в ноль или полученные деньги.
|
|
||||||
Дата дивиденда обозначена как дата реестра. Offer и погашение сохраняют своё
|
|
||||||
значение без суммы выплаты. У каждой строки видны тип,
|
|
||||||
инструмент, дата и доступная сумма в исходной валюте. Список не имеет
|
инструмент, дата и доступная сумма в исходной валюте. Список не имеет
|
||||||
пагинации и не конвертирует валюты. Пустой результат означает отсутствие
|
пагинации и не конвертирует валюты. Пустой результат означает отсутствие
|
||||||
известных поддерживаемых событий в выбранном окне, а не доказательство
|
известных поддерживаемых событий в выбранном окне, а не доказательство
|
||||||
отсутствия всех будущих выплат. Инструмент без поддерживаемых данных
|
отсутствия всех будущих выплат. Инструмент без поддерживаемых данных
|
||||||
не получает искусственную строку «нет данных».
|
не получает искусственную строку «нет данных».
|
||||||
|
|
||||||
Сохранённый 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; полный экран показывает список без общего итога.
|
||||||
|
|
||||||
|
Текущие controller, сервис и экран поддерживают выбор периода, тогда как
|
||||||
|
[основная спецификация](../../../specs/openspec/specs/brokerage-account-upcoming-events/spec.md#requirement-account-page-presents-an-upcoming-events-list)
|
||||||
|
описывает фиксированные 30 дней и экран без фильтров. Документация отражает
|
||||||
|
реализованный выбор периода, не изменяя это несогласованное требование.
|
||||||
|
|
||||||
## Получение и отказы
|
## Получение и отказы
|
||||||
|
|
||||||
Запрос требует сессии, подключения и локально подтверждённого membership.
|
```mermaid
|
||||||
Он читает сохранённый forecast и не запускает личную синхронизацию. Часовой
|
sequenceDiagram
|
||||||
обход строит forecast из полного личного portfolio и общих календарей: T-Bank
|
participant F as Frontend
|
||||||
для дивидендов, MOEX для bond events. Личные оценки сохраняются отдельно от
|
participant B as Backend
|
||||||
общих календарей.
|
participant T as T-Bank
|
||||||
|
participant M as MOEX ISS
|
||||||
|
F->>B: Счёт и выбранный период событий
|
||||||
|
B->>B: Проверяет сессию и текущее подключение
|
||||||
|
B->>T: Получает открытые счета и позиции доступного счёта
|
||||||
|
par События акций
|
||||||
|
B->>T: GetDividends за выбранный период
|
||||||
|
T-->>B: Известные дивиденды
|
||||||
|
and События облигаций
|
||||||
|
B->>M: Календарь bondization
|
||||||
|
M-->>B: Купоны, погашения и оферты
|
||||||
|
end
|
||||||
|
B->>B: Применяет период и текущее количество бумаг
|
||||||
|
B-->>F: Полный список известных событий либо ошибка
|
||||||
|
```
|
||||||
|
|
||||||
Последний полный forecast привязан к scope, исходному окну и membership. Если
|
Запрос требует действующей сессии и подключения T-Bank. Получение
|
||||||
часовой обход не может получить полный новый forecast, прежний полный прогноз,
|
необходимых финансовых данных брокера или календаря MOEX завершается ошибкой
|
||||||
его `calculatedAt` и исходное окно остаются читаемыми, в том числе после
|
`503`, если источник недоступен: частичный список не выдаётся как полный.
|
||||||
рестарта; `meta.lastAttempt` отдельно сообщает об ошибке обновления. Без
|
Доступные метаданные инструментов дополняют название и логотип; когда
|
||||||
полного forecast `data: null` и metadata обозначает initial loading либо
|
исходного тикера достаточно для календаря, ошибка дополнительного поиска
|
||||||
временную недоступность, а не подтверждённое отсутствие выплат. В режиме `all`
|
метаданных не скрывает события.
|
||||||
результат complete только при полном вкладе каждого включённого счёта. Другой
|
|
||||||
scope или изменившийся membership не получают старый forecast под новым
|
|
||||||
названием.
|
|
||||||
Успешный ответ MOEX без запрошенного блока означает отсутствие поддерживаемых
|
|
||||||
событий этого источника; это отличается от 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)
|
||||||
|
|||||||
@@ -89,11 +89,7 @@ HTML-источник, общий кэш и поведение при измен
|
|||||||
|
|
||||||
Календарь охватывает пять прошлых лет и следующий год. Будущие купоны
|
Календарь охватывает пять прошлых лет и следующий год. Будущие купоны
|
||||||
сортируются по дате выплаты, прошедшие и недатированные доступны отдельно.
|
сортируются по дате выплаты, прошедшие и недатированные доступны отдельно.
|
||||||
Будущие и прошедшие события купонного графика показываются единым предметным
|
Показаны сумма на облигацию, номер, тип, длительность периода и дата реестра,
|
||||||
представлением выплаты с пометкой события по графику — тем же, что у
|
|
||||||
исполненных выплат и прогноза; исполнение подтверждает только личная история.
|
|
||||||
Показаны сумма на облигацию, отдельная личная оценка, номер, тип, длительность
|
|
||||||
периода и дата реестра,
|
|
||||||
если они известны. Неопределённый будущий купон не заменяется нулём.
|
если они известны. Неопределённый будущий купон не заменяется нулём.
|
||||||
|
|
||||||
Личная оценка доступна только при положительных позициях, известной сумме,
|
Личная оценка доступна только при положительных позициях, известной сумме,
|
||||||
@@ -115,42 +111,34 @@ HTML-источник, общий кэш и поведение при измен
|
|||||||
транспортного контракта задаёт [HTTP API](../../architecture/http-api.md).
|
транспортного контракта задаёт [HTTP API](../../architecture/http-api.md).
|
||||||
|
|
||||||
`BondBy`, `FindInstrument` и `GetBondCoupons` используют способ
|
`BondBy`, `FindInstrument` и `GetBondCoupons` используют способ
|
||||||
декодирования, сохраняющий отсутствие полей. Общие данные выпуска, quotes,
|
декодирования, сохраняющий отсутствие полей. Рыночные RPC проходят
|
||||||
календари, MOEX metrics и необходимые результаты НКР сохраняются в SQLite.
|
общие TLS, очередь, ограничение частоты, deadline и повторные попытки. Кэш успешных и совместных
|
||||||
Личные позиции и оценки выплат используют текущий доступ и сохранённый
|
незавершённых запросов ограничен LRU и сроком 30 секунд по пользователю,
|
||||||
portfolio; они не попадают в общие источники.
|
поколению подключения и параметрам. Ошибки не кэшируются; ошибки авторизации
|
||||||
|
не подавляются при дополнительной загрузке данных поиска.
|
||||||
|
Исходный ответ `BondBy` совместно используется поиском, карточкой, котировкой,
|
||||||
|
графиком, купонами и сводкой показателей в пределах того же пользователя, поколения и TTL.
|
||||||
|
Одновременная загрузка секций вызывает один `BondBy` на UID. Календарь
|
||||||
|
и сводка совместно используют один запрос купонов; котировка переиспользуется
|
||||||
|
при расчёте купонной доходности. MOEX и НКР загружаются только для открытого
|
||||||
|
выпуска, независимо от поиска. Для 30 новых
|
||||||
|
результатов поиск выполняет 32 RPC до повторных попыток: один `FindInstrument`,
|
||||||
|
30 `BondBy` и один пакетный запрос цен. Общий ограничитель backend не заменяет
|
||||||
|
квоты Т-Банка; при их исчерпании сохраняется `TBANK_UNAVAILABLE`.
|
||||||
|
|
||||||
BondBy переиспользуется зависимостями выпуска. Discovery принадлежит query,
|
Разделы загружаются независимо, показывают каркас загрузки и локальную повторную попытку.
|
||||||
свежесть — конкретному source/range. Missing/stale зависимости ставятся
|
|
||||||
в фоновую очередь через прежний limiter; обычный HTTP-ответ не ждёт брокера.
|
|
||||||
Покрытые завершённые свечи не перечитываются после restart. Открытый хвост
|
|
||||||
остаётся изменяемым; ручной refresh охватывает показанный период.
|
|
||||||
|
|
||||||
Последняя полная личная оценка сохраняется при ошибке новых обязательных
|
|
||||||
зависимостей, вместе с исходным временем. Неполнота не означает отсутствие
|
|
||||||
выплат. Наблюдение статуса не запускает повторный импорт. Политика источников
|
|
||||||
и границы — в [модели свежести](../../architecture/cross-cutting/data-freshness.md).
|
|
||||||
|
|
||||||
Разделы загружаются независимо, показывают каркас загрузки и локальное
|
|
||||||
спокойное состояние недоступности без действия повтора.
|
|
||||||
Ошибка фонового обновления сохраняет последние данные. Смена подключения
|
Ошибка фонового обновления сохраняет последние данные. Смена подключения
|
||||||
сбрасывает поддерево клиентских запросов облигаций вместе с остальными финансовыми запросами;
|
сбрасывает поддерево клиентских запросов облигаций вместе с остальными финансовыми запросами;
|
||||||
выход очищает кэш сессии. Общие график, позиция и личные события
|
выход очищает кэш сессии. Общие график, позиция и личные события
|
||||||
имеют общее представление на страницах акций и облигаций.
|
имеют общее представление на страницах акций и облигаций.
|
||||||
|
|
||||||
Основные спецификации [портфеля](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/portfolio-view/spec.md)
|
Основные спецификации [портфеля](../../../specs/openspec/specs/portfolio-view/spec.md)
|
||||||
и [журнала](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/brokerage-account-ledger/spec.md)
|
и [журнала](../../../specs/openspec/specs/brokerage-account-ledger/spec.md)
|
||||||
описывают исходные финансовые понятия. Отдельная основная спецификация
|
описывают исходные финансовые понятия. Отдельная основная спецификация
|
||||||
страницы облигации в текущем OpenSpec Store не найдена; условия вычислений
|
страницы облигации в текущем OpenSpec Store не найдена; условия вычислений
|
||||||
подтверждены [каталогом](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/bonds/application/bond-catalog.service.ts),
|
подтверждены [каталогом](../../../backend/src/modules/investments/instruments/bonds/application/bond-catalog.service.ts),
|
||||||
[рыночными данными](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/bonds/application/bond-market.service.ts),
|
[рыночными данными](../../../backend/src/modules/investments/instruments/bonds/application/bond-market.service.ts),
|
||||||
[выплатами](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/bonds/application/bond-income.service.ts)
|
[выплатами](../../../backend/src/modules/investments/instruments/bonds/application/bond-income.service.ts)
|
||||||
и [проверками](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/bonds/application/bond.spec.ts).
|
и [проверками](../../../backend/src/modules/investments/instruments/bonds/application/bond.spec.ts).
|
||||||
Границы источников описаны в [разделе интеграций](../../integrations/README.md),
|
Границы источников описаны в [разделе интеграций](../../integrations/README.md),
|
||||||
сходное разделение рыночных и личных данных — в [акциях](stocks.md).
|
сходное разделение рыночных и личных данных — в [акциях](stocks.md).
|
||||||
|
|
||||||
## Online reference
|
|
||||||
|
|
||||||
Страница облигации, поиск и переход по тикеру обращаются к онлайн-справочнику
|
|
||||||
брокера; сохранённые quotes, свечи, события, ratings и метки портфеля
|
|
||||||
сохраняют собственные границы свежести и ошибок.
|
|
||||||
|
|||||||
@@ -28,30 +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. Однобуквенные тикеры также поддерживаются.
|
||||||
Прямая ссылка не требует предварительного открытия портфеля. Хлебные крошки
|
Прямая ссылка не требует предварительного открытия портфеля. Хлебные крошки
|
||||||
«Акции → название» возвращают к скринеру.
|
«Акции → название» возвращают к поиску.
|
||||||
|
|
||||||
## Содержание и состояния
|
## Содержание и состояния
|
||||||
|
|
||||||
@@ -64,9 +58,8 @@
|
|||||||
рыночные данные и личная история остаются доступны.
|
рыночные данные и личная история остаются доступны.
|
||||||
|
|
||||||
Каждый раздел загружается независимо. Первичная загрузка показывает каркас
|
Каждый раздел загружается независимо. Первичная загрузка показывает каркас
|
||||||
раздела, затянувшаяся недоступность — локальное спокойное состояние без
|
раздела, ошибка — локальную повторную попытку. При ошибке фонового обновления
|
||||||
повторной попытки. При ошибке фонового обновления сохраняются предыдущие
|
сохраняются предыдущие данные и предупреждение. Цена содержит собственное время
|
||||||
данные без технического предупреждения. Цена содержит собственное время
|
|
||||||
UTC, которое может быть старше времени получения ответа. Статус торгов не
|
UTC, которое может быть старше времени получения ответа. Статус торгов не
|
||||||
выводится из свежести цены. Отсутствующее значение не заменяется нулём.
|
выводится из свежести цены. Отсутствующее значение не заменяется нулём.
|
||||||
|
|
||||||
@@ -80,8 +73,7 @@ Frontend не получает брокерский токен и не обра
|
|||||||
|
|
||||||
Личная история использует существующий
|
Личная история использует существующий
|
||||||
`GET /api/v1/brokerage-accounts/all/operations?instrumentUid=...`.
|
`GET /api/v1/brokerage-accounts/all/operations?instrumentUid=...`.
|
||||||
Фильтр UID валидируется и применяется к локальной истории. При ручной
|
Фильтр UID валидируется и передаётся upstream как `instrumentId`; ответ также
|
||||||
сверке бумаги он также передаётся upstream как `instrumentId`; ответ
|
|
||||||
проверяется по UID. Без `from` такой запрос начинается с самой ранней известной
|
проверяется по UID. Без `from` такой запрос начинается с самой ранней известной
|
||||||
даты открытия доступных счетов; если даты неполны, нижняя граница — Unix epoch.
|
даты открытия доступных счетов; если даты неполны, нижняя граница — Unix epoch.
|
||||||
Обычный журнал без фильтра инструмента сохраняет начало текущего года.
|
Обычный журнал без фильтра инструмента сохраняет начало текущего года.
|
||||||
@@ -126,11 +118,7 @@ UID включён в ключ кэша и подпись общего курс
|
|||||||
|
|
||||||
## Дивиденды и финансовые показатели
|
## Дивиденды и финансовые показатели
|
||||||
|
|
||||||
События компании отделены от личных зачислений. Будущие и прошедшие события
|
События компании отделены от личных зачислений. Известные будущие выплаты
|
||||||
дивидендного графика показываются единым предметным представлением выплаты
|
|
||||||
с пометкой «событие по графику» — тем же, что у исполненных выплат и прогноза;
|
|
||||||
сумма на акцию, отдельная личная оценка и дата реестра сохраняют свои подписи
|
|
||||||
и не выдаются за личное зачисление. Известные будущие выплаты
|
|
||||||
определяются по `paymentDate`, при её отсутствии — по `recordDate`; прошедшие
|
определяются по `paymentDate`, при её отсутствии — по `recordDate`; прошедшие
|
||||||
и недатированные события помещены в отдельную историю компании. Отменённые
|
и недатированные события помещены в отдельную историю компании. Отменённые
|
||||||
события исключаются. Личная оценка умножает сумму на акцию на текущее положительное
|
события исключаются. Личная оценка умножает сумму на акцию на текущее положительное
|
||||||
@@ -154,27 +142,18 @@ TTM означает последние 12 месяцев, MRQ — послед
|
|||||||
|
|
||||||
## Получение данных и ограничения
|
## Получение данных и ограничения
|
||||||
|
|
||||||
Общие metadata, discovery, company, quotes, свечи и календари сохраняются
|
Запросы T-Bank проходят общую серверную интеграцию с TLS, лимитом частоты,
|
||||||
в SQLite. Скринер получает каталог online и показывает время его получения;
|
очередью, предельным временем ожидания и повторными попытками. Проверка доступа
|
||||||
временная недоступность каталога не подтверждает отсутствие подходящих акций.
|
и расшифровка token выполняются только backend. Успешные ответы и совместные незавершённые запросы кэшируются
|
||||||
Отсутствующее enrichment не скрывает найденную бумагу. Сохранённые общие
|
в ограниченном кэше LRU на 30 секунд по пользователю, поколению подключения и параметрам.
|
||||||
источники переиспользуются между пользователями, но личные позиции и выплаты
|
Для поиска `FindInstrument` дополняется сведениями `ShareBy` через тот же
|
||||||
всегда проверяют доступ.
|
кэш карточки инструмента и одним пакетным `GetLastPrices` для списка. Ошибка
|
||||||
|
дополнительной загрузки или исчезнувшая карточка не скрывает исходную запись
|
||||||
Обычное чтение не ждёт RPC; missing/stale зависимости ставятся в фоновую
|
каталога; недоступные логотипы и цены имеют заглушки. Ошибки авторизации
|
||||||
очередь через существующий T-Bank limiter. Часовая свежесть применяется
|
не подавляются.
|
||||||
к quotes и открытому хвосту свечей; metadata/company/календари, будучи
|
Ошибка целого запроса не кэшируется; отсутствие дополнительного источника
|
||||||
сохранёнными, не перечитываются обычным чтением.
|
обозначается в ответе. Поддерево клиентских запросов акций сбрасывается вместе с финансовыми
|
||||||
Закрытые полные свечные интервалы, включая пустые, сохраняются после restart.
|
данными при изменении подключения; выход очищает кэш сессии.
|
||||||
Новый день не делает прежнюю незавершённую свечу полной.
|
|
||||||
|
|
||||||
Отдельного действия «Обновить» на странице нет: чтение текущего инструмента,
|
|
||||||
выбранного графика и personal-history scope выполняется автоматически.
|
|
||||||
Частичные строки видны с обозначением неполноты; неполные личные оценки
|
|
||||||
не становятся нулями. Статус и `sync=observe` не создают новый импорт.
|
|
||||||
Смена подключения и известный access loss очищают личное представление;
|
|
||||||
поздние ответы не возвращают прежние данные. Детали —
|
|
||||||
[модель свежести](../../architecture/cross-cutting/data-freshness.md).
|
|
||||||
|
|
||||||
Полные даты в интерфейсе имеют формат ДД.ММ.ГГГГ; ISO-формат транспорта и
|
Полные даты в интерфейсе имеют формат ДД.ММ.ГГГГ; ISO-формат транспорта и
|
||||||
временные зоны расчётов не меняются. Личная история использует общий с портфелем формат строки операции,
|
временные зоны расчётов не меняются. Личная история использует общий с портфелем формат строки операции,
|
||||||
@@ -190,17 +169,11 @@ TTM означает последние 12 месяцев, MRQ — послед
|
|||||||
История открытых счетов не обещает полную историю владения, включая закрытые
|
История открытых счетов не обещает полную историю владения, включая закрытые
|
||||||
счета, переводы и универсальные корпоративные события.
|
счета, переводы и универсальные корпоративные события.
|
||||||
|
|
||||||
Основные спецификации [портфеля](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/portfolio-view/spec.md)
|
Основные спецификации [портфеля](../../../specs/openspec/specs/portfolio-view/spec.md)
|
||||||
и [журнала](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/brokerage-account-ledger/spec.md)
|
и [журнала](../../../specs/openspec/specs/brokerage-account-ledger/spec.md)
|
||||||
задают исходные финансовые понятия. Отдельная основная спецификация страницы
|
задают исходные финансовые понятия. Отдельная основная спецификация страницы
|
||||||
акции в текущем OpenSpec Store не найдена; описанные механизмы проверяются
|
акции в текущем OpenSpec Store не найдена; описанные механизмы проверяются
|
||||||
по [каталогу](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/stocks/application/stock-catalog.service.ts),
|
по [каталогу](../../../backend/src/modules/investments/instruments/stocks/application/stock-catalog.service.ts),
|
||||||
[рыночным данным](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/stocks/application/stock-market.service.ts),
|
[рыночным данным](../../../backend/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).
|
[выплатам](../../../backend/src/modules/investments/instruments/stocks/application/stock-income.service.ts) и [проверкам](../../../backend/src/modules/investments/instruments/stocks/application/stock.spec.ts).
|
||||||
Сопоставление с облигациями — на [странице облигаций](bonds.md).
|
Сопоставление с облигациями — на [странице облигаций](bonds.md).
|
||||||
|
|
||||||
## Online reference
|
|
||||||
|
|
||||||
Страница акции, поиск и переход по тикеру обращаются к онлайн-справочнику
|
|
||||||
брокера; сохранённые позиции, quotes, свечи и события остаются независимыми
|
|
||||||
локальными ресурсами и не подменяются ошибкой справочника.
|
|
||||||
|
|||||||
@@ -46,9 +46,8 @@ ID операций разных счетов не означают дублик
|
|||||||
|
|
||||||
На [страницах акций](../instruments/stocks.md) и
|
На [страницах акций](../instruments/stocks.md) и
|
||||||
[облигаций](../instruments/bonds.md) этот же журнал становится личной историей
|
[облигаций](../instruments/bonds.md) этот же журнал становится личной историей
|
||||||
конкретной бумаги. Фильтр `instrumentUid` применяется к локальным операциям. Ручная сверка
|
конкретной бумаги. Фильтр `instrumentUid` передаётся брокеру и проверяется
|
||||||
личной истории может ограничить источниковый запрос этой бумагой, не присваивая
|
в ответе. Без явного начала периода он охватывает историю с открытия счёта;
|
||||||
такому subset полноту всех операций счёта. Без явного начала периода он охватывает историю с открытия счёта;
|
|
||||||
в общем режиме — с самой ранней известной даты открытия доступных счетов.
|
в общем режиме — с самой ранней известной даты открытия доступных счетов.
|
||||||
Если даты открытия неполны, нижняя граница — Unix epoch. Это позволяет
|
Если даты открытия неполны, нижняя граница — Unix epoch. Это позволяет
|
||||||
видеть прошлые операции уже проданной бумаги, но не обещает историю
|
видеть прошлые операции уже проданной бумаги, но не обещает историю
|
||||||
@@ -56,38 +55,50 @@ ID операций разных счетов не означают дублик
|
|||||||
|
|
||||||
## Получение и полнота
|
## Получение и полнота
|
||||||
|
|
||||||
`GET /api/v1/brokerage-accounts/:accountId/operations` читает сохранённые
|
```mermaid
|
||||||
операции и coverage после локальной проверки доступа. Период, категории,
|
sequenceDiagram
|
||||||
UID, cursor и limit применяются к этой выборке. Транспорт описан в
|
participant F as Frontend
|
||||||
[HTTP API](../../architecture/http-api.md).
|
participant B as Backend
|
||||||
|
participant T as T-Bank
|
||||||
|
F->>B: Запрашивает применённый период и страницу операций
|
||||||
|
B->>B: Проверяет сессию и текущее подключение
|
||||||
|
B->>T: Получает открытые счета
|
||||||
|
T-->>B: Подтверждает доступность счёта
|
||||||
|
alt Подходящая страница есть в кэше
|
||||||
|
B-->>F: Сохранённая страница
|
||||||
|
else Нужна загрузка
|
||||||
|
B->>T: GetOperationsByCursor, только исполненные операции
|
||||||
|
T-->>B: Страница операций
|
||||||
|
B->>B: Нормализует записи и применяет фильтры
|
||||||
|
B-->>F: Записи и продолжение либо ошибка
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
Стабильность broker operation ID в пределах счёта и подключения принята как
|
`GET /api/v1/brokerage-accounts/:accountId/operations` требует Bearer token.
|
||||||
допущение проекта. Повторный ID обновляет ту же запись без двойного учёта.
|
Период, категории, необязательный UID, курсор и размер страницы задают запрос;
|
||||||
Страницы источника сначала сохраняются в staging. Полный успешный обход
|
ответ содержит записи, время наблюдения и сведения о продолжении.
|
||||||
разрешает upsert, удаление исчезнувших записей в перечитанном predicate
|
Точные поля и допустимые значения задаёт [HTTP API](../../architecture/http-api.md).
|
||||||
и публикацию coverage одной транзакцией. При повторной сверке полного
|
|
||||||
показанного периода транзакция охватывает все его окна и счета.
|
|
||||||
|
|
||||||
Полнота относится к UTC-интервалу `[from,to)`, включая подтверждённый пустой.
|
Backend хранит не более 100 страниц в памяти процесса. Свежая страница
|
||||||
Категории, размер страницы и `hasNext=false` её не определяют. При первичном
|
живёт до 30 секунд; при переполнении вытесняется давно не использовавшаяся.
|
||||||
импорте подтверждённые строки видны как partial; итоги ждут полного периода.
|
Ключ разделяет пользователя, поколение подключения, счёт, нормализованный
|
||||||
Догрузка остальной истории обозначается отдельно. Без известной нижней
|
период и категории, UID инструмента, курсор и размер страницы.
|
||||||
границы нельзя заявить полную историю счёта.
|
Отсутствующий конец периода приводится к текущему 30-секундному интервалу.
|
||||||
|
Текущее подключение и доступ к счёту проверяются до выдачи страницы из кэша.
|
||||||
|
|
||||||
Cursor связан с пользователем, подключением, membership для `all`, фильтрами
|
После успешного изменения подключения страницы предыдущего поколения,
|
||||||
и ревизией набора операций. Изменение строк даёт `INVALID_OPERATIONS_CURSOR`;
|
в том числе поздно завершившиеся запросы, не выдаются журналу или аналитике
|
||||||
клиент возвращается на первую страницу, сохраняя применённые фильтры.
|
дохода. Они удаляются по обычным ограничениям времени и объёма.
|
||||||
Coverage/status без изменения rowset cursor не ломают. Перезапуск сохраняет
|
Локальной синхронизации и долговременного хранения операций нет.
|
||||||
опубликованные интервалы; незавершённая работа возобновляется только запросом.
|
Продолжения [общего журнала](../portfolio/all-accounts.md#общая-пагинация-операций)
|
||||||
|
имеют собственные ограничения и восстановление после истечения.
|
||||||
|
|
||||||
## Представление и ошибки
|
## Представление и ошибки
|
||||||
|
|
||||||
Полный журнал и превью используют одинаковую компактную строку: логотип
|
Полный журнал и превью используют одинаковую компактную строку: логотип
|
||||||
или буквенная заглушка, описание, дата, инструмент и денежное движение.
|
или буквенная заглушка, описание, дата, инструмент и денежное движение.
|
||||||
Исполненные купоны и дивиденды показываются единым предметным представлением
|
Для купона и дивиденда денежная метка называет точное событие; для прочих
|
||||||
выплаты — тем же, что на странице дохода, в прогнозе и графиках инструментов;
|
операций — категорию. Она показывается один раз, без повтора рядом с датой.
|
||||||
денежная метка называет точное исполненное событие. Для прочих
|
|
||||||
операций метка называет категорию. Она показывается один раз, без повтора рядом с датой.
|
|
||||||
Знак и цвет обозначают направление денег; отсутствие движения нейтрально.
|
Знак и цвет обозначают направление денег; отсутствие движения нейтрально.
|
||||||
Если описание отсутствует, показывается локализованный тип операции.
|
Если описание отсутствует, показывается локализованный тип операции.
|
||||||
|
|
||||||
@@ -99,9 +110,8 @@ Coverage/status без изменения rowset cursor не ломают. Пе
|
|||||||
не заменяется каркасом при применении действий или фоновом обновлении.
|
не заменяется каркасом при применении действий или фоновом обновлении.
|
||||||
Ошибки `TBANK_NOT_CONNECTED`, `INVALID_TBANK_TOKEN`, недоступный счёт (`404`)
|
Ошибки `TBANK_NOT_CONNECTED`, `INVALID_TBANK_TOKEN`, недоступный счёт (`404`)
|
||||||
и `TBANK_UNAVAILABLE` сохраняют [общую семантику API](../../architecture/http-api.md).
|
и `TBANK_UNAVAILABLE` сохраняют [общую семантику API](../../architecture/http-api.md).
|
||||||
Только полный пустой ответ означает отсутствие подходящих операций.
|
Пустой успешный ответ означает отсутствие подходящих операций, а не сбой.
|
||||||
Partial empty сообщает о неполноте; source failure сохраняет прежний результат.
|
|
||||||
|
|
||||||
Согласованное поведение описано в [спецификации журнала](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/brokerage-account-ledger/spec.md).
|
Согласованное поведение описано в [спецификации журнала](../../../specs/openspec/specs/brokerage-account-ledger/spec.md).
|
||||||
Фильтр конкретной бумаги и общий режим расширяют исходное описание отдельного
|
Фильтр конкретной бумаги и общий режим расширяют исходное описание отдельного
|
||||||
счёта; их механизм раскрыт на страницах инструментов и общего портфеля.
|
счёта; их механизм раскрыт на страницах инструментов и общего портфеля.
|
||||||
|
|||||||
@@ -51,50 +51,69 @@ Backend публикует статические маршруты под `/api/
|
|||||||
используют прежнюю карточку. Доля относится к позициям той же валюты
|
используют прежнюю карточку. Доля относится к позициям той же валюты
|
||||||
во всём показанном портфеле, без денежных остатков.
|
во всём показанном портфеле, без денежных остатков.
|
||||||
- Журнал объединяет операции по убыванию времени, при равном времени использует
|
- Журнал объединяет операции по убыванию времени, при равном времени использует
|
||||||
порядок идентификаторов счетов и операций. ID
|
порядок идентификаторов счетов, сохраняя порядок внутри одного счёта. ID
|
||||||
операций разных счетов не считаются дублями. Переводы не схлопываются.
|
операций разных счетов не считаются дублями. Переводы не схлопываются.
|
||||||
- Доходы собираются из всех страниц операций каждого счёта за выбранный период.
|
- Доходы собираются из всех страниц операций каждого счёта за общие периоды.
|
||||||
Итоги, временные интервалы и принадлежность выплат рассчитывает
|
Итоги, сравнения, временные интервалы и принадлежность выплат рассчитывает
|
||||||
сервис дохода. Данные сравнения периодов не загружаются и не возвращаются;
|
сервис дохода.
|
||||||
состав счетов влияет только на фактические выплаты выбранного периода.
|
Для сравнения с годом ранее используется текущий состав счетов; новый счёт
|
||||||
|
добавляет только реально полученные выплаты. При известных датах открытия всех счетов
|
||||||
|
доступность сравнения определяется самым ранним открытием в этом составе.
|
||||||
|
Закрытые счета в историческое сравнение не добавляются.
|
||||||
- Будущие события сортируются по дате и сохраняют отдельную строку каждого счёта.
|
- Будущие события сортируются по дате и сохраняют отдельную строку каждого счёта.
|
||||||
Прогноз не включается в реализованные доходы.
|
Прогноз не включается в реализованные доходы.
|
||||||
|
|
||||||
## Полнота, свежесть и доступ
|
## Полнота, свежесть и доступ
|
||||||
|
|
||||||
Общий запрос читает membership и разрешённые snapshots из SQLite в одной
|
Для каждого общего запроса backend определяет состав доступных счетов
|
||||||
согласованной границе. Время источников может различаться: это не обещание
|
и единое поколение подключения. Получение данных использует эту же границу подключения;
|
||||||
одновременного наблюдения счетов у брокера. Неполный счёт делает aggregate
|
проверки сессии, доступа и существующий лимитер внешних запросов сохраняются.
|
||||||
неполным; отсутствующие суммы не подменяются нулями. `lastSuccessfulAt`
|
Финансовые данные не сохраняются в БД; снимки и продолжения хранятся
|
||||||
показывает самое раннее время обязательных источников.
|
в памяти процесса.
|
||||||
|
|
||||||
Секции портфеля, операций, дохода и событий готовы независимо. Ошибка
|
Ошибка источника не превращается в частичный успешный ответ раздела.
|
||||||
обновления сохраняет прежний полный результат того же scope и периода.
|
Портфель может использовать разрешённый существующей политикой запасной снимок
|
||||||
Известный отказ доступа и смена подключения очищают personal view.
|
отдельного счёта. Тогда общий `isStale` равен `true`, а `lastSuccessfulAt`
|
||||||
|
показывает самое раннее время среди включённых снимков. Для пустого состава
|
||||||
|
возвращается корректный пустой результат. Общий запрос не является атомарным
|
||||||
|
наблюдением всех счетов в один момент времени.
|
||||||
|
|
||||||
|
Ошибка блока операций, доходов или будущих событий не скрывает основной
|
||||||
|
портфель. При фоновом обновлении страницы уже полученный результат сохраняется
|
||||||
|
с индикатором или предупреждением и повторной попыткой. Смена сессии или
|
||||||
|
подключения сбрасывает общий режим вместе с остальными финансовыми запросами.
|
||||||
|
|
||||||
## Общая пагинация операций
|
## Общая пагинация операций
|
||||||
|
|
||||||
Страница строится из локальных строк с порядком occurredAt, accountId и ID
|
Сервис объединяет потоки страниц каждого счёта и сохраняет непрочитанные строки
|
||||||
операции. Cursor проверяет пользователя, подключение, membershipRevision,
|
между запросами. Фильтрованные пустые страницы не обрывают поток. Повреждённая
|
||||||
фильтры, limit и ревизии операций. Изменение rowset возвращает
|
цепочка курсоров и нарушенный порядок дат возвращают ошибку без частичной страницы.
|
||||||
`400 INVALID_OPERATIONS_CURSOR`; клиент сбрасывает стек страниц, сохраняя
|
|
||||||
применённые фильтры. Coverage/status без изменения строк не ломают cursor.
|
|
||||||
Продолжение не зависит от RAM-кэша или перезапуска.
|
|
||||||
|
|
||||||
Повторная сверка полного показанного периода публикует все счета и интервалы
|
Курсор общего журнала — случайный непрозрачный идентификатор продолжения,
|
||||||
одной транзакцией; успешное первое окно не смешивается с прежними остальными.
|
привязанного к пользователю, поколению подключения, составу счетов, фильтрам
|
||||||
Подробнее — [журнал](../operations/overview.md#получение-и-полнота).
|
и размеру страницы. Верхняя граница периода фиксируется первым запросом.
|
||||||
|
Состояние ограничено 100 продолжениями на процесс со сроком жизни пять минут;
|
||||||
|
повторный запрос того же продолжения возвращает тот же успешный результат.
|
||||||
|
|
||||||
|
Неизвестный, вытесненный, истёкший либо несовместимый курсор возвращает
|
||||||
|
`400 INVALID_OPERATIONS_CURSOR`. Действие «Повторить» сбрасывает пагинацию,
|
||||||
|
обновляет первую страницу и сохраняет фильтры. Перезапуск backend также
|
||||||
|
инвалидирует продолжения. Раздел рассчитан на один экземпляр backend, как
|
||||||
|
существующий финансовый кэш.
|
||||||
|
|
||||||
## Совместимость и границы требований
|
## Совместимость и границы требований
|
||||||
|
|
||||||
Общий режим использует те же persistent sources и envelope, что и отдельный
|
Общий режим добавляет маршруты, сохраняя маршруты отдельных счетов.
|
||||||
счёт. Новый envelope несовместим с прежним bare response: backend и frontend
|
При совместном обновлении сначала нужен backend, затем frontend с
|
||||||
поставляются согласованной парой по [HTTP contract](../../architecture/http-api.md).
|
соответствующим сгенерированным контрактом; прежний клиент продолжает
|
||||||
Additive migration сохраняет прежние таблицы; rollback приложения не удаляет
|
работать с отдельными счетами. При откате сначала возвращается клиент.
|
||||||
новые данные и не заменяет рабочую базу старым backup. Возврат требует заранее
|
Этот механизм не требует изменения схемы SQLite и миграции финансовых данных.
|
||||||
проверенной совместимой пары; произвольный прежний клиент не считается совместимым.
|
|
||||||
Процедура backup/restore описана в [runbook](../../architecture/deployment.md).
|
|
||||||
|
|
||||||
Основные спецификации областей сохраняют исходные понятия отдельного счёта.
|
Основные спецификации [портфеля](../../../specs/openspec/specs/portfolio-view/spec.md),
|
||||||
Утверждённый change `persist-tbank-data` распространяет полноту, локальный
|
[операций](../../../specs/openspec/specs/brokerage-account-ledger/spec.md),
|
||||||
доступ, атомарную сверку и состояния синхронизации на режим `all`.
|
[дохода](../../../specs/openspec/specs/brokerage-account-income-analytics/spec.md)
|
||||||
|
и [событий](../../../specs/openspec/specs/brokerage-account-upcoming-events/spec.md)
|
||||||
|
описывают отдельный счёт. Общий режим подтверждён действующими маршрутизаторами,
|
||||||
|
сервисами и [проверками агрегации](../../../backend/src/modules/investments/all-accounts.spec.ts),
|
||||||
|
но не выделен в самостоятельную основную спецификацию. Не следует считать
|
||||||
|
описание общего режима изменением требований к отдельному счёту.
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
| Cash Balance | Суммы из `GetPositions.money`, объединённые по валюте | Заблокированные деньги не прибавляются; торгуемый инструмент `CURRENCY` остаётся позицией |
|
| Cash Balance | Суммы из `GetPositions.money`, объединённые по валюте | Заблокированные деньги не прибавляются; торгуемый инструмент `CURRENCY` остаётся позицией |
|
||||||
| `totalsByCurrency` | Стоимости позиций + доступные деньги той же валюты | Не включает невычислимую стоимость; не обещает полную оценку портфеля |
|
| `totalsByCurrency` | Стоимости позиций + доступные деньги той же валюты | Не включает невычислимую стоимость; не обещает полную оценку портфеля |
|
||||||
| `lastSuccessfulAt` | Время формирования успешного снимка, UTC | Не является временем каждой котировки; запасной снимок сохраняет исходное время |
|
| `lastSuccessfulAt` | Время формирования успешного снимка, UTC | Не является временем каждой котировки; запасной снимок сохраняет исходное время |
|
||||||
| `isStale` | Истёк часовой срок свежести сохранённого portfolio source | Не описывает состояние клиентского кэша или ошибку фонового запроса |
|
| `isStale` | Backend вернул предыдущий полный снимок после временной ошибки | Не описывает состояние клиентского кэша или ошибку фонового запроса |
|
||||||
|
|
||||||
Ненулевые денежные остатки одной валюты суммируются. Позиции одинаковых
|
Ненулевые денежные остатки одной валюты суммируются. Позиции одинаковых
|
||||||
инструментов в ответе отдельного счёта не объединяются; визуальная группировка
|
инструментов в ответе отдельного счёта не объединяются; визуальная группировка
|
||||||
@@ -56,8 +56,8 @@ UID определяет конкретный инструмент брокер
|
|||||||
ISIN, тикер и `logoName` могут быть `null`. `logoName` — имя файла бренда,
|
ISIN, тикер и `logoName` могут быть `null`. `logoName` — имя файла бренда,
|
||||||
а не URL: frontend строит ссылку на публичный CDN. Ошибка изображения
|
а не URL: frontend строит ссылку на публичный CDN. Ошибка изображения
|
||||||
или метаданных не меняет количество, суммы и свежесть снимка и не скрывает
|
или метаданных не меняет количество, суммы и свежесть снимка и не скрывает
|
||||||
финансовые данные. Metadata обновляется независимо; отсутствие имени не задерживает выдачу
|
финансовые данные. Имя ожидается перед завершением нового снимка;
|
||||||
финансового снимка и оставляет доступный идентификатор.
|
ошибка поиска оставляет доступный идентификатор.
|
||||||
|
|
||||||
## Структура и доли
|
## Структура и доли
|
||||||
|
|
||||||
@@ -90,8 +90,8 @@ frontend показывает до сотых. Положительное зна
|
|||||||
поэтому ограничение долей действует на все валюты. В общем портфеле проверка
|
поэтому ограничение долей действует на все валюты. В общем портфеле проверка
|
||||||
отрицательных денег выполняется по остаткам счетов до взаимозачёта.
|
отрицательных денег выполняется по остаткам счетов до взаимозачёта.
|
||||||
|
|
||||||
Нормативные основы: [нормализованный состав](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/portfolio-view/spec.md#requirement-нормализованный-состав-portfolio)
|
Нормативные основы: [нормализованный состав](../../../specs/openspec/specs/portfolio-view/spec.md#requirement-нормализованный-состав-portfolio)
|
||||||
и [точные значения и валютные агрегаты](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/portfolio-view/spec.md#requirement-точные-значения-и-валютные-агрегаты).
|
и [точные значения и валютные агрегаты](../../../specs/openspec/specs/portfolio-view/spec.md#requirement-точные-значения-и-валютные-агрегаты).
|
||||||
Структура и общий режим подтверждены реализацией и её проверками;
|
Структура и общий режим подтверждены реализацией и её проверками;
|
||||||
отдельных требований к ним в основной спецификации портфеля пока нет.
|
отдельных требований к ним в основной спецификации портфеля пока нет.
|
||||||
Поля транспорта следует сверять с [HTTP API](../../architecture/http-api.md),
|
Поля транспорта следует сверять с [HTTP API](../../architecture/http-api.md),
|
||||||
|
|||||||
@@ -1,47 +1,158 @@
|
|||||||
# Загрузка портфеля
|
# Загрузка портфеля
|
||||||
|
|
||||||
Экран `/accounts/:accountId` читает последний полный snapshot из SQLite после
|
Поток начинается при открытии `/accounts/:accountId` пользователем
|
||||||
проверки сессии, подключения и локального membership. Если список счетов ещё
|
с действующей сессией. Его результат — полный нормализованный снимок
|
||||||
неизвестен, сервер сообщает о загрузке доступа без выдачи личных данных
|
доступного счёта либо объяснимая ошибка. Браузер обращается к backend;
|
||||||
неизвестного ID. Известный недоступный счёт возвращает `404`.
|
доступ к брокерскому токену и внешние запросы остаются на сервере.
|
||||||
|
|
||||||
## Основной обмен
|
## Основной обмен
|
||||||
|
|
||||||
Обычный GET и retry возвращают read-only локальный `{ data, meta }` envelope:
|
Сначала frontend проверяет свой успешный кэш: в течение 30 секунд повторное
|
||||||
они не ждут и не вызывают T-Bank и не предлагают ручную синхронизацию. Часовой
|
открытие может показать ответ без нового HTTP-запроса. Следующая схема
|
||||||
обход текущего подключения обновляет Portfolio независимо от экрана. Для нового
|
описывает запрос, который действительно дошёл до backend.
|
||||||
полного snapshot ему нужны оба финансовых ответа `GetPortfolio` и
|
|
||||||
`GetPositions`; суммы и позиции публикуются вместе. Metadata инструмента
|
|
||||||
дополняет представление независимо, с доступным UID/FIGI вместо имени до её
|
|
||||||
онлайн-загрузки.
|
|
||||||
|
|
||||||
Открытый экран выполняет локальное перечитывание не чаще одного раза в
|
```mermaid
|
||||||
30 минут; retry также повторяет только HTTP GET. `data: null` означает, что
|
sequenceDiagram
|
||||||
полного snapshot ещё нет, а не нулевой портфель. Пустой подтверждённый portfolio
|
participant F as Frontend
|
||||||
остаётся available complete result. Секции могут быть готовы независимо.
|
participant B as Backend API
|
||||||
Подробнее — [свежесть](../../../architecture/cross-cutting/data-freshness.md).
|
participant D as SQLite
|
||||||
|
participant T as T-Bank
|
||||||
|
F->>B: Запрос портфеля счёта с Bearer token
|
||||||
|
B->>B: Проверяет сессию
|
||||||
|
B->>D: Читает текущее подключение пользователя
|
||||||
|
D-->>B: Подключение либо отсутствие
|
||||||
|
alt Подключения нет
|
||||||
|
B-->>F: 409 TBANK_NOT_CONNECTED
|
||||||
|
else Подключение доступно
|
||||||
|
B->>B: Определяет поколение подключения
|
||||||
|
alt Снимок того же поколения не старше 30 секунд
|
||||||
|
B-->>F: Полный снимок, isStale=false
|
||||||
|
else Нужен новый снимок
|
||||||
|
B->>T: Получает открытые счета
|
||||||
|
T-->>B: Состав доступных счетов
|
||||||
|
alt Счёт недоступен
|
||||||
|
B-->>F: 404
|
||||||
|
else Счёт доступен
|
||||||
|
par Финансовые позиции
|
||||||
|
B->>T: GetPortfolio
|
||||||
|
T-->>B: Позиции и цены
|
||||||
|
and Доступные деньги
|
||||||
|
B->>T: GetPositions
|
||||||
|
T-->>B: Денежные остатки
|
||||||
|
end
|
||||||
|
B->>T: Метаданные отсутствующих инструментов по UID, затем FIGI
|
||||||
|
T-->>B: Метаданные, отсутствие или ошибка
|
||||||
|
B->>B: Нормализует состав и точно вычисляет суммы
|
||||||
|
B-->>F: Полный новый снимок, isStale=false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
## Границы доступа
|
Свежий серверный снимок выдаётся после разрешения текущего сохранённого
|
||||||
|
подключения, но без нового `GetAccounts`. После истечения свежего кэша
|
||||||
|
проверка доступности счёта предшествует финансовым RPC. SQLite хранит
|
||||||
|
подключение, а не снимок или право владения счётом вне этого подключения.
|
||||||
|
|
||||||
Публикация проверяет connection revision и attempt в той же транзакции. Замена
|
Для нового снимка требуются оба финансовых ответа. Backend не выдаёт
|
||||||
или удаление подключения не допускает позднюю запись прежней работы. Frontend
|
позиции без денег или деньги без позиций как полный портфель.
|
||||||
очищает недоступный personal view и отклоняет запоздалые ответы. В режиме
|
Метаданные дополняют имя и идентификаторы, но их временная недоступность
|
||||||
[«Все счета»](../all-accounts.md#полнота-свежесть-и-доступ) состав accounts и
|
не срывает финансовый результат. Поиск выполняется партиями по пять позиций,
|
||||||
snapshots читается согласованно, но их source times могут различаться.
|
чтобы крупный портфель не переполнял очередь одновременными запросами.
|
||||||
|
Порядок позиций сохраняется. Backend ищет сведения по UID, затем по FIGI,
|
||||||
|
если UID не дал пригодного названия или тикера.
|
||||||
|
Ожидание метаданных ограничено 30 секундами на снимок счёта. После этого
|
||||||
|
используется доступный кэш либо идентификатор, новые поиски для снимка
|
||||||
|
не запускаются. Уже начатые запросы могут закончиться позже и обновить общий
|
||||||
|
справочный кэш, но не меняют выданный финансовый снимок.
|
||||||
|
Общий серверный клиент обеспечивает TLS, очередь,
|
||||||
|
ограничение частоты, deadline RPC и разрешённые повторные попытки —
|
||||||
|
[граница интеграции](../../../integrations/tbank.md).
|
||||||
|
|
||||||
|
## Время и изменение подключения
|
||||||
|
|
||||||
|
| Слой | Что сохраняется | Условие повторного использования |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Frontend | Последний успешный HTTP-ответ для выбранного счёта и сессии | Свежим считается 30 секунд; при обычном обновлении уже показанный ответ остаётся видимым |
|
||||||
|
| Backend | Полный снимок пары пользователь–счёт в памяти процесса | До 30 секунд, только в том же поколении текущего подключения |
|
||||||
|
| Backend при временной ошибке | Тот же полный снимок | Не старше пяти минут с момента успешного сохранения, только в том же поколении |
|
||||||
|
| Метаданные инструмента | Успешный поиск или подтверждённое отсутствие | Свежими считаются 24 часа; общий кэш на 500 ключей UID/FIGI, ошибки не сохраняются |
|
||||||
|
|
||||||
|
Это параметры текущей реализации, а не самостоятельная политика этого
|
||||||
|
документа: [снимки](../../../../backend/src/modules/investments/portfolio/application/portfolio.service.ts),
|
||||||
|
[метаданные](../../../../backend/src/integrations/tbank/instrument-metadata.service.ts) и
|
||||||
|
[клиентский запрос](../../../../frontend/src/entities/portfolio/api/usePortfolio.ts).
|
||||||
|
|
||||||
|
Пять минут — предельный возраст успешного снимка, а не дополнительные
|
||||||
|
пять минут после окончания свежих 30 секунд. Запасной ответ сохраняет
|
||||||
|
исходное `lastSuccessfulAt` и получает `isStale: true`.
|
||||||
|
|
||||||
|
После истечения 24 часов backend обновляет справочные данные. Если обновление
|
||||||
|
временно недоступно, ранее успешные сведения остаются запасным вариантом,
|
||||||
|
пока запись есть в ограниченном кэше процесса. Ошибка не продлевает срок
|
||||||
|
свежести: следующий поиск снова пытается обновить запись. Это относится только
|
||||||
|
к названию, тикеру, ISIN и логотипу, а не к ценам или денежным значениям.
|
||||||
|
Перезапуск backend и вытеснение записи из кэша удаляют этот запасной вариант.
|
||||||
|
Неполный ответ без названия и тикера также не затирает известные сведения
|
||||||
|
и не обновляет срок их свежести. Подтверждённое отсутствие инструмента
|
||||||
|
заменяет прежнюю запись отрицательным результатом поиска.
|
||||||
|
|
||||||
|
Время снимка фиксируется при получении финансовых ответов, до ожидания
|
||||||
|
названий. Сроки серверного кэша отсчитываются от этого времени, поэтому
|
||||||
|
долгая загрузка справочных данных не делает старые суммы новыми.
|
||||||
|
Успешные справочные данные общие для всех пользователей; выполняющиеся
|
||||||
|
запросы объединяются только внутри одного брокерского токена, чтобы ошибка
|
||||||
|
его авторизации не передавалась другому подключению.
|
||||||
|
|
||||||
|
Успешное создание, замена или удаление подключения меняет границу
|
||||||
|
повторного использования финансовых данных. Старое или поздно завершившееся
|
||||||
|
получение снимка не делает его доступным новым запросам другого поколения.
|
||||||
|
Явная очистка памяти при изменении подключения дополняет эту проверку.
|
||||||
|
Frontend сбрасывает всё поддерево финансовых запросов при изменении
|
||||||
|
подключения и общий кэш сессии при выходе; старый портфель не служит
|
||||||
|
заглушкой загрузки нового подключения.
|
||||||
|
|
||||||
|
В общем режиме сначала определяется состав доступных счетов одного
|
||||||
|
подключения, затем получаются их снимки. Это
|
||||||
|
[не атомарное наблюдение нескольких счетов](../all-accounts.md#полнота-свежесть-и-доступ).
|
||||||
|
|
||||||
## Ошибки и результат для пользователя
|
## Ошибки и результат для пользователя
|
||||||
|
|
||||||
Нет подключения — `409 TBANK_NOT_CONNECTED`, известный отказ токена —
|
| Причина | Ответ backend | Действие на экране |
|
||||||
`400 INVALID_TBANK_TOKEN`, недоступный счёт — `404`. Они не маскируются
|
| --- | --- | --- |
|
||||||
запасным snapshot. Временная ошибка часового обхода оставляет прежний полный
|
| Нет подключения | `409 TBANK_NOT_CONNECTED` | Открыть настройки T-Bank |
|
||||||
portfolio и его исходное `lastSuccessfulAt`; `meta.lastAttempt` сообщает о
|
| Брокер отклонил токен | `400 INVALID_TBANK_TOKEN` | Открыть настройки; токен автоматически не удаляется |
|
||||||
failed или partial попытке отдельно. `Portfolio.isStale` сообщает возраст
|
| Счёт недоступен через текущее подключение | `404` | Вернуться к списку счетов |
|
||||||
snapshot, а Client Cached Snapshot и ошибка браузерного HTTP-перечитывания
|
| Временная ошибка, пригодный предыдущий снимок есть | `200`, `isStale: true` | Показать снимок и его исходное время с предупреждением |
|
||||||
имеют независимый смысл. При отсутствии полного snapshot `data: null` и
|
| Временная ошибка, пригодного снимка нет | `503 TBANK_UNAVAILABLE` | Повторить загрузку |
|
||||||
metadata обозначает initial loading либо временную недоступность, без нулевых
|
| Неизвестная ошибка | Безопасная ошибка API | Повторить загрузку |
|
||||||
итогов. `503` означает ошибку обслуживания самого HTTP-запроса, а не
|
|
||||||
завершившуюся фоновую работу.
|
|
||||||
|
|
||||||
Денежные правила находятся в [справочнике](../data-reference.md), проверка
|
`INVALID_TBANK_TOKEN` и `404` не заменяются серверным запасным снимком.
|
||||||
расхождения — в [диагностике](../how-to/investigate-discrepancy.md), RPC/лимиты
|
Временная ошибка определения доступных счетов также может использовать
|
||||||
— в [интеграции T-Bank](../../../integrations/tbank.md).
|
пригодный снимок того же поколения. При временном сбое метаданных сначала
|
||||||
|
используются ранее успешные сведения, а при их отсутствии — идентификатор
|
||||||
|
инструмента; это не устанавливает `isStale`. Отказ авторизации при поиске
|
||||||
|
метаданных не маскируется запасным названием.
|
||||||
|
|
||||||
|
Для запроса самого портфеля на клиенте нет polling, автоматического повтора или
|
||||||
|
обновления при возврате фокуса. При обновлении того же уже показанного
|
||||||
|
результата клиент оставляет его на экране, добавляя индикатор загрузки
|
||||||
|
или предупреждение с доступной с клавиатуры повторной попыткой.
|
||||||
|
Это клиентский сохранённый ответ, независимо от серверного `isStale`.
|
||||||
|
|
||||||
|
Frontend отображает название и остальные сведения из ответа портфеля.
|
||||||
|
Повторные попытки поиска для акций, облигаций и других типов инструментов
|
||||||
|
принадлежат [общему клиенту T-Bank на backend](../../../integrations/tbank.md#конфигурация-и-ограничения).
|
||||||
|
|
||||||
|
Первое обращение к крупному портфелю может потребовать много метаданных.
|
||||||
|
Переполнение общей очереди RPC может произойти при конкурирующих запросах;
|
||||||
|
поиск метаданных в этом случае повторяется с ограниченными паузами. После
|
||||||
|
успешного поиска метаданные используются повторно до истечения
|
||||||
|
кэша. Значение `TBANK_RATE_LIMIT_PER_SECOND` следует соотносить с квотой
|
||||||
|
источника; его увеличение не является гарантированным исправлением
|
||||||
|
ограничения самого T-Bank.
|
||||||
|
|
||||||
|
Проверяемые правила: [свежесть и запасной снимок](../../../../specs/openspec/specs/portfolio-view/spec.md#requirement-свежесть-и-fallback-полного-portfolio-snapshot),
|
||||||
|
[клиентские состояния](../../../../specs/openspec/specs/portfolio-view/spec.md#requirement-состояния-и-ошибки-portfolio-на-клиенте).
|
||||||
|
Для расследования конкретного значения используйте
|
||||||
|
[порядок диагностики](../how-to/investigate-discrepancy.md).
|
||||||
|
|||||||
@@ -12,13 +12,12 @@
|
|||||||
величины. Убедитесь, что сравниваемые значения относятся к одному составу
|
величины. Убедитесь, что сравниваемые значения относятся к одному составу
|
||||||
счетов и инструменту. Название или тикер сами по себе не доказывают
|
счетов и инструменту. Название или тикер сами по себе не доказывают
|
||||||
совпадение выпуска; используйте доступные UID, FIGI и ISIN.
|
совпадение выпуска; используйте доступные UID, FIGI и ISIN.
|
||||||
2. **Сопоставьте время.** Проверьте `lastSuccessfulAt`, `isStale` и показанный
|
2. **Сопоставьте время.** Проверьте `lastSuccessfulAt`, `isStale` и наличие
|
||||||
исход последней часовой попытки. Серверный снимок не обещает время
|
предупреждения о клиентском обновлении. Серверный снимок не обещает время
|
||||||
каждой котировки, а общий портфель может соединять разные моменты.
|
каждой котировки, а общий портфель может соединять разные моменты.
|
||||||
Действия «Повторить» на экране нет: временный сбой чтения восстанавливается
|
Если запрос завершился ошибкой, доступное действие «Повторить» должно
|
||||||
автоматически, поэтому расхождение проверяйте по показанному состоянию
|
обновить результат или оставить объяснимую ошибку. Перезагрузка backend
|
||||||
источника, а не по результату ручного обновления. Перезагрузка backend
|
удаляет кэш и не восстанавливает историю стоимости.
|
||||||
сохраняет последний полный снимок в SQLite, но не создаёт историю стоимости.
|
|
||||||
3. **Проверьте состав суммы.** Для одной валюты итог складывается из
|
3. **Проверьте состав суммы.** Для одной валюты итог складывается из
|
||||||
вычислимых стоимостей позиций и доступных денег. Не добавляйте деньги
|
вычислимых стоимостей позиций и доступных денег. Не добавляйте деньги
|
||||||
второй раз, не складывайте валюты и не включайте заблокированные средства.
|
второй раз, не складывайте валюты и не включайте заблокированные средства.
|
||||||
@@ -38,16 +37,16 @@
|
|||||||
теряет цифры, валюту или показатель, исследуйте frontend.
|
теряет цифры, валюту или показатель, исследуйте frontend.
|
||||||
Если исходные безопасные данные и нормализованный ответ расходятся,
|
Если исходные безопасные данные и нормализованный ответ расходятся,
|
||||||
исследуйте backend. Если не совпадают ожидаемое и описанное поведение,
|
исследуйте backend. Если не совпадают ожидаемое и описанное поведение,
|
||||||
сначала сопоставьте [спецификацию портфеля](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/portfolio-view/spec.md)
|
сначала сопоставьте [спецификацию портфеля](../../../../specs/openspec/specs/portfolio-view/spec.md)
|
||||||
и ограничения [потока загрузки](../flows/portfolio-loading.md).
|
и ограничения [потока загрузки](../flows/portfolio-loading.md).
|
||||||
Исправление документации само по себе не разрешает конфликт требований
|
Исправление документации само по себе не разрешает конфликт требований
|
||||||
и реализации.
|
и реализации.
|
||||||
|
|
||||||
Для адресной проверки исходников владельцами служат
|
Для адресной проверки исходников владельцами служат
|
||||||
[сервис снимков](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/portfolio/application/portfolio.service.ts),
|
[сервис снимков](../../../../backend/src/modules/investments/portfolio/application/portfolio.service.ts),
|
||||||
[валютные итоги](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/portfolio/infrastructure/tbank-portfolio.mapper.ts),
|
[валютные итоги](../../../../backend/src/modules/investments/portfolio/infrastructure/tbank-portfolio.mapper.ts),
|
||||||
[структура](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/portfolio/domain/portfolio-structure.ts) и
|
[структура](../../../../backend/src/modules/investments/portfolio/domain/portfolio-structure.ts) и
|
||||||
[подготовка экрана](https://git.personal.ksv741.ru/WealthLedger/frontend/src/commit/9f7e342333b80bfacfd5bc394a2dcd0613d33ff3/src/pages/account/ui/AccountPage.vm.ts).
|
[подготовка экрана](../../../../frontend/src/pages/account/ui/AccountPage.vm.ts).
|
||||||
Это точки продолжения расследования; смысл вычислений изложен выше.
|
Это точки продолжения расследования; смысл вычислений изложен выше.
|
||||||
|
|
||||||
Разбор завершён, когда различие объяснено составом, временем или определением
|
Разбор завершён, когда различие объяснено составом, временем или определением
|
||||||
|
|||||||
@@ -17,11 +17,11 @@
|
|||||||
[справочнике данных](data-reference.md).
|
[справочнике данных](data-reference.md).
|
||||||
|
|
||||||
Доступность счёта определяется [текущим подключением и списком счетов](../accounts/overview.md).
|
Доступность счёта определяется [текущим подключением и списком счетов](../accounts/overview.md).
|
||||||
Backend получает данные через серверную
|
SQLite не хранит сам портфель: backend получает данные через серверную
|
||||||
[интеграцию T-Bank](../../integrations/tbank.md) и постоянно сохраняет последний
|
[интеграцию T-Bank](../../integrations/tbank.md) и временно сохраняет успешный
|
||||||
полный снимок в SQLite. Frontend получает нормализованный результат через HTTP API.
|
снимок в памяти. Frontend получает нормализованный результат через HTTP API.
|
||||||
[Поток загрузки](flows/portfolio-loading.md) объясняет границы доступа,
|
[Поток загрузки](flows/portfolio-loading.md) объясняет границы доступа,
|
||||||
свежесть снимка и восстановление после временной ошибки.
|
свежий кэш и восстановление после временной ошибки.
|
||||||
|
|
||||||
Обзор счёта также показывает пять последних [операций](../operations/overview.md),
|
Обзор счёта также показывает пять последних [операций](../operations/overview.md),
|
||||||
[полученный доход](../income/overview.md) текущего месяца и до пяти
|
[полученный доход](../income/overview.md) текущего месяца и до пяти
|
||||||
@@ -64,7 +64,7 @@ Backend получает данные через серверную
|
|||||||
без логотипа и с подписью результата. Отсутствующие показатели скрываются,
|
без логотипа и с подписью результата. Отсутствующие показатели скрываются,
|
||||||
а не подменяются нулём или заглушкой.
|
а не подменяются нулём или заглушкой.
|
||||||
|
|
||||||
Существует расхождение: [согласованная спецификация представления позиций](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/portfolio-view/spec.md#requirement-представление-карточек-позиций-на-клиенте)
|
Существует расхождение: [согласованная спецификация представления позиций](../../../specs/openspec/specs/portfolio-view/spec.md#requirement-представление-карточек-позиций-на-клиенте)
|
||||||
требует доступности всех иных типов в отдельной секции, а текущие frontend
|
требует доступности всех иных типов в отдельной секции, а текущие frontend
|
||||||
и его проверка исключают `CURRENCY` только из списка карточек. Такие позиции
|
и его проверка исключают `CURRENCY` только из списка карточек. Такие позиции
|
||||||
остаются в ответе, валютных итогах, структуре и знаменателе долей остальных
|
остаются в ответе, валютных итогах, структуре и знаменателе долей остальных
|
||||||
@@ -79,26 +79,21 @@ Backend получает данные через серверную
|
|||||||
не содержат данных и не получают фокус. При обычном фоновом обновлении
|
не содержат данных и не получают фокус. При обычном фоновом обновлении
|
||||||
сохраняются предыдущие суммы и время успешного получения в UTC.
|
сохраняются предыдущие суммы и время успешного получения в UTC.
|
||||||
|
|
||||||
Серверный `isStale` обозначает возраст снимка старше часа: обновлением
|
Серверный `isStale` обозначает разрешённый запасной снимок backend.
|
||||||
владеет часовой обход, а не запрос экрана. Он показывается рядом с суммами
|
Клиентское предупреждение означает неудачную попытку обновить уже показанный
|
||||||
вместе с временем последнего успешного результата и исходом последней часовой
|
ответ. Эти состояния независимы и могут появиться одновременно.
|
||||||
попытки. Неудачная попытка клиента обновить уже показанный ответ о себе не
|
Успешный повторный запрос обновляет суммы и время, снимает клиентскую ошибку;
|
||||||
сообщает: эти состояния независимы, но пользователю видимо только состояние
|
серверное предупреждение определяется новым `isStale`.
|
||||||
источника. Успешное автоматическое перечитывание обновляет суммы и время;
|
|
||||||
обозначение устаревания определяется новым `isStale`.
|
|
||||||
Правила времени и ошибок приведены в [потоке загрузки](flows/portfolio-loading.md).
|
Правила времени и ошибок приведены в [потоке загрузки](flows/portfolio-loading.md).
|
||||||
|
|
||||||
Отдельного чтения истории стоимости нет, но каждый полный снимок
|
В текущем решении нет долговременной истории снимков, графиков стоимости
|
||||||
сохраняет точку оценки по счёту и валюте для будущих поддерживаемых
|
портфеля, валютной конвертации, учёта виртуальных позиций или заблокированных
|
||||||
представлений; публичного графика в текущем решении нет. Нет также
|
средств. Кэш теряется при перезапуске и рассчитан на локальную работу одного
|
||||||
валютной конвертации, учёта виртуальных позиций или заблокированных
|
экземпляра backend. Отдельной команды принудительного обновления на экране
|
||||||
средств. Последний полный снимок сохраняется после перезапуска;
|
нет; повторная попытка доступна после ошибки.
|
||||||
обновлением личных данных владеет часовой обход, рассчитанный на один
|
|
||||||
экземпляр backend. Действие «Обновить» выполняет только локальное
|
|
||||||
перечитывание открытого экрана и не запускает работу с T-Bank.
|
|
||||||
|
|
||||||
Для проверки конкретной суммы используйте
|
Для проверки конкретной суммы используйте
|
||||||
[руководство по расхождениям](how-to/investigate-discrepancy.md).
|
[руководство по расхождениям](how-to/investigate-discrepancy.md).
|
||||||
Проверяемое поведение отдельного счёта описано в
|
Проверяемое поведение отдельного счёта описано в
|
||||||
[спецификации портфеля](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/portfolio-view/spec.md);
|
[спецификации портфеля](../../../specs/openspec/specs/portfolio-view/spec.md);
|
||||||
точную форму HTTP-ответа задаёт [OpenAPI backend](../../architecture/http-api.md).
|
точную форму HTTP-ответа задаёт [OpenAPI backend](../../architecture/http-api.md).
|
||||||
|
|||||||
+2
-42
@@ -4,14 +4,6 @@
|
|||||||
Связи между ними объясняет [обзор продукта](overview.md), подробные правила —
|
Связи между ними объясняет [обзор продукта](overview.md), подробные правила —
|
||||||
[предметные области](README.md#предметные-области).
|
[предметные области](README.md#предметные-области).
|
||||||
|
|
||||||
## Релиз развёртывания WealthLedger
|
|
||||||
|
|
||||||
Версия комплекта установки WealthLedger, закрепляющая проверенную совместимую
|
|
||||||
пару релизов frontend и backend. Понятие принято в
|
|
||||||
[ADR 016](adr/016-versioned-deployment-bundle.md).
|
|
||||||
|
|
||||||
_Не использовать_: релиз frontend или backend как синоним релиза всей установки.
|
|
||||||
|
|
||||||
## User
|
## User
|
||||||
|
|
||||||
Локальный владелец данных WealthLedger, идентифицируемый `id` и
|
Локальный владелец данных WealthLedger, идентифицируемый `id` и
|
||||||
@@ -128,14 +120,6 @@ Instrument обогащают отображение Position или Event, но
|
|||||||
|
|
||||||
_Не использовать_: бумага, тикер, актив позиции.
|
_Не использовать_: бумага, тикер, актив позиции.
|
||||||
|
|
||||||
## Скринер акций
|
|
||||||
|
|
||||||
Подбор выпусков акций по заданным условиям из каталога T-Bank с указанием
|
|
||||||
времени получения списка. Скринер отличается от текстового поиска конкретного
|
|
||||||
инструмента, личного портфеля и подтверждения исполнимости сделки.
|
|
||||||
|
|
||||||
_Не использовать_: гарантия покупки, личная проверка доступа к сделке.
|
|
||||||
|
|
||||||
## Instrument Logo
|
## Instrument Logo
|
||||||
|
|
||||||
Имя файла бренда T-Bank для отображения Instrument в интерфейсе;
|
Имя файла бренда T-Bank для отображения Instrument в интерфейсе;
|
||||||
@@ -160,15 +144,6 @@ _Не использовать_: валютная позиция, заблоки
|
|||||||
|
|
||||||
_Не использовать_: история портфеля, snapshot для графика.
|
_Не использовать_: история портфеля, snapshot для графика.
|
||||||
|
|
||||||
## Точка истории стоимости портфеля
|
|
||||||
|
|
||||||
Полная оценка одного Brokerage Account в одной валюте на момент успешного
|
|
||||||
обновления Portfolio; последовательность таких точек описывает изменение
|
|
||||||
оценки во времени, включая неторговые дни. Не является Portfolio Snapshot
|
|
||||||
или инвестиционной доходностью; отсутствие цены позиции не приравнивается к нулю.
|
|
||||||
|
|
||||||
_Не использовать_: снимок состава портфеля, доходность.
|
|
||||||
|
|
||||||
## Client Cached Snapshot
|
## Client Cached Snapshot
|
||||||
|
|
||||||
Технический термин: последний успешно полученный frontend-ответ React Query, который остаётся
|
Технический термин: последний успешно полученный frontend-ответ React Query, который остаётся
|
||||||
@@ -194,19 +169,6 @@ _Не использовать_: история Portfolio, баланс счёт
|
|||||||
|
|
||||||
_Не использовать_: проводка, рассчитанная операция.
|
_Не использовать_: проводка, рассчитанная операция.
|
||||||
|
|
||||||
## Полнота истории счёта
|
|
||||||
|
|
||||||
Подтверждённость того, что за указанный период загружены все доступные
|
|
||||||
исполненные операции счёта на момент успешной сверки с брокером. Полный период
|
|
||||||
может не содержать операций; полнота не гарантирует отсутствия последующих
|
|
||||||
исправлений брокера и отличается от свежести данных.
|
|
||||||
|
|
||||||
Понятие принято в [ADR 017](adr/017-local-tbank-reads-and-background-sync.md).
|
|
||||||
Текущее хранение coverage и независимая свежесть объяснены в
|
|
||||||
[модели данных](architecture/cross-cutting/data-freshness.md).
|
|
||||||
|
|
||||||
_Не использовать_: свежесть истории, отсутствие операций как признак полноты.
|
|
||||||
|
|
||||||
## Operation Category
|
## Operation Category
|
||||||
|
|
||||||
Нормализованная категория Ledger Entry для фильтрации и отображения:
|
Нормализованная категория Ledger Entry для фильтрации и отображения:
|
||||||
@@ -216,15 +178,13 @@ _Не использовать_: upstream operation type как category.
|
|||||||
|
|
||||||
## Ledger Page Cache
|
## Ledger Page Cache
|
||||||
|
|
||||||
Исторический технический термин: RAM-кэш страницы Account Ledger.
|
Технический термин: кэш страницы Account Ledger, отделённый от кэша отдельных операций.
|
||||||
Текущее чтение использует сохранённые операции и coverage в SQLite;
|
|
||||||
страница не является единицей полноты.
|
|
||||||
|
|
||||||
_Не использовать_: operations cache, кэш отдельных операций.
|
_Не использовать_: operations cache, кэш отдельных операций.
|
||||||
|
|
||||||
## Ledger Page Cache Key
|
## Ledger Page Cache Key
|
||||||
|
|
||||||
Исторический ключ RAM-кэша, разделявший страницы по пользователю, подключению,
|
Ключ, разделяющий страницы журнала по пользователю, текущему подключению,
|
||||||
счёту и параметрам запроса, включая необязательный UID инструмента.
|
счёту и параметрам запроса, включая необязательный UID инструмента.
|
||||||
Полный состав и правила нормализации описаны в
|
Полный состав и правила нормализации описаны в
|
||||||
[журнале операций](domains/operations/overview.md#получение-и-полнота).
|
[журнале операций](domains/operations/overview.md#получение-и-полнота).
|
||||||
|
|||||||
@@ -48,6 +48,6 @@ HTTP-запрос ограничен таймаутом. Сеть, неуспе
|
|||||||
Наличие успешного HTTP-ответа не гарантирует котировку без задержки.
|
Наличие успешного HTTP-ответа не гарантирует котировку без задержки.
|
||||||
|
|
||||||
Условия обработки источника в прогнозе закреплены в
|
Условия обработки источника в прогнозе закреплены в
|
||||||
[спецификации будущих событий](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/brokerage-account-upcoming-events/spec.md#requirement-upcoming-events-use-complete-source-results).
|
[спецификации будущих событий](../../specs/openspec/specs/brokerage-account-upcoming-events/spec.md#requirement-upcoming-events-use-complete-source-results).
|
||||||
Точный механизм транспорта, разбора и сопоставления реализован в
|
Точный механизм транспорта, разбора и сопоставления реализован в
|
||||||
[клиенте MOEX](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/integrations/moex/moex.client.ts).
|
[клиенте MOEX](../../backend/src/integrations/moex/moex.client.ts).
|
||||||
|
|||||||
+1
-1
@@ -36,7 +36,7 @@ ISIN, даты и ссылки на выпуск. Изменение табли
|
|||||||
После ошибки новая загрузка ограничена минутным интервалом; внутри одного
|
После ошибки новая загрузка ограничена минутным интервалом; внутри одного
|
||||||
HTTP-запроса повторных попыток нет. Обновление запускается следующим обращением
|
HTTP-запроса повторных попыток нет. Обновление запускается следующим обращением
|
||||||
после истечения кэша, без фонового задания. Точный механизм и сроки принадлежат
|
после истечения кэша, без фонового задания. Точный механизм и сроки принадлежат
|
||||||
[клиенту НКР](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/integrations/ncr/ncr-ratings.client.ts).
|
[клиенту НКР](../../backend/src/integrations/ncr/ncr-ratings.client.ts).
|
||||||
|
|
||||||
При отказе реестра показатели облигации отмечают недоступность рейтинга,
|
При отказе реестра показатели облигации отмечают недоступность рейтинга,
|
||||||
сохраняя остальные доступные показатели. Готовый ответ метрик имеет
|
сохраняя остальные доступные показатели. Готовый ответ метрик имеет
|
||||||
|
|||||||
+19
-30
@@ -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), смысл дополнений — в
|
||||||
@@ -42,7 +36,7 @@ Backend передаёт имя файла `logoName` из метаданных
|
|||||||
Логотипы используются в портфеле, операциях, будущих событиях, поиске и
|
Логотипы используются в портфеле, операциях, будущих событиях, поиске и
|
||||||
страницах инструментов. Отсутствующее имя или ошибка изображения оставляют
|
страницах инструментов. Отсутствующее имя или ошибка изображения оставляют
|
||||||
буквенную заглушку и не меняют финансовые значения. Правило построения URL
|
буквенную заглушку и не меняют финансовые значения. Правило построения URL
|
||||||
принадлежит [форматированию frontend](https://git.personal.ksv741.ru/WealthLedger/frontend/src/commit/9f7e342333b80bfacfd5bc394a2dcd0613d33ff3/src/shared/lib/display.ts).
|
принадлежит [форматированию frontend](../../frontend/src/shared/lib/display.ts).
|
||||||
|
|
||||||
## Проверка и сохранение подключения
|
## Проверка и сохранение подключения
|
||||||
|
|
||||||
@@ -68,7 +62,7 @@ sequenceDiagram
|
|||||||
B->>T: GetAccounts OPEN по gRPC over TLS
|
B->>T: GetAccounts OPEN по gRPC over TLS
|
||||||
alt Проверка успешна, в том числе при пустом списке счетов
|
alt Проверка успешна, в том числе при пустом списке счетов
|
||||||
T-->>B: Доступные открытые счета
|
T-->>B: Доступные открытые счета
|
||||||
B->>D: Атомарно сохранить token, новую revision и membership
|
B->>D: Создать или заменить шифротекст AES-256-GCM
|
||||||
B-->>F: Подключено и время сохранения
|
B-->>F: Подключено и время сохранения
|
||||||
F->>F: Обновить статус и сбросить финансовые выборки
|
F->>F: Обновить статус и сбросить финансовые выборки
|
||||||
F-->>U: Показать подтверждённое подключение
|
F-->>U: Показать подтверждённое подключение
|
||||||
@@ -97,18 +91,12 @@ sequenceDiagram
|
|||||||
|
|
||||||
`INVALID_TBANK_TOKEN` означает отказ внешней аутентификации или прав доступа.
|
`INVALID_TBANK_TOKEN` означает отказ внешней аутентификации или прав доступа.
|
||||||
`TBANK_UNAVAILABLE` сообщает о временной или иной upstream-ошибке, включая
|
`TBANK_UNAVAILABLE` сообщает о временной или иной upstream-ошибке, включая
|
||||||
исчерпание очереди. Диагностические причины жизненного цикла часовой попытки
|
исчерпание очереди. Ответы и ошибки не содержат token. Удаление подключения
|
||||||
(`INTERRUPTED` — прерывание до нормального завершения, `DEADLINE` — истечение
|
идемпотентно: удаляется локальный секрет и прекращается его использование
|
||||||
ограничения ожидания шага) не относятся к ошибкам T-Bank: они остаются
|
для последующих запросов. Оно не описывает отзыв token на стороне T-Bank.
|
||||||
внутренней диагностикой и не раскрываются в hourly-read. Истечение ограничения
|
|
||||||
ожидания не отменяет уже начатые обращения к T-Bank; их поздние результаты
|
|
||||||
остаются в строках шагов. Ответы и ошибки не содержат token. Удаление подключения
|
|
||||||
идемпотентно: удаляются локальный секрет, личные datasets и work; поздняя
|
|
||||||
публикация прежней revision запрещена. Общие неперсональные источники могут
|
|
||||||
сохраняться. Оно не описывает отзыв token на стороне T-Bank.
|
|
||||||
|
|
||||||
Согласованное поведение сохранено в workspace-спецификации
|
Согласованное поведение сохранено в workspace-спецификации
|
||||||
[T-Bank connection](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/tbank-connection/spec.md).
|
[T-Bank connection](../../specs/openspec/specs/tbank-connection/spec.md).
|
||||||
Точные endpoints и формы ответов определяет backend OpenAPI;
|
Точные endpoints и формы ответов определяет backend OpenAPI;
|
||||||
[HTTP API](../architecture/http-api.md) описывает общую границу.
|
[HTTP API](../architecture/http-api.md) описывает общую границу.
|
||||||
|
|
||||||
@@ -118,7 +106,7 @@ sequenceDiagram
|
|||||||
использования финансовых данных. Это относится и к сохранению того же самого
|
использования финансовых данных. Это относится и к сохранению того же самого
|
||||||
token: предыдущее подключение не становится источником данных нового.
|
token: предыдущее подключение не становится источником данных нового.
|
||||||
Неуспешная операция сохраняет прежнее подключение и его доступные данные.
|
Неуспешная операция сохраняет прежнее подключение и его доступные данные.
|
||||||
Механизм внутренней revision, поздние ответы и сброс клиентских выборок объясняет
|
Механизм generation, поздние ответы и сброс клиентских выборок объясняет
|
||||||
[свежесть данных](../architecture/cross-cutting/data-freshness.md#смена-подключения).
|
[свежесть данных](../architecture/cross-cutting/data-freshness.md#смена-подключения).
|
||||||
|
|
||||||
## Конфигурация и ограничения
|
## Конфигурация и ограничения
|
||||||
@@ -138,29 +126,30 @@ Backend хранит token в шифротексте AES-256-GCM с новым n
|
|||||||
| `TBANK_CA_CERT_PATH` | Необязательный путь к доверенному корневому CA в PEM для сетей с TLS-инспекцией; проверка TLS сохраняется |
|
| `TBANK_CA_CERT_PATH` | Необязательный путь к доверенному корневому CA в PEM для сетей с TLS-инспекцией; проверка TLS сохраняется |
|
||||||
|
|
||||||
Параметры окружения определяет backend
|
Параметры окружения определяет backend
|
||||||
[validateEnv](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/config/env.validation.ts) (справочный источник).
|
[validateEnv](../../backend/src/config/env.validation.ts) (workspace-ссылка).
|
||||||
Общий FIFO-лимитер ограничивает поток RPC и не допускает более 50
|
Общий FIFO-лимитер ограничивает поток RPC и не допускает более 50
|
||||||
ожидающих. Для `UNAVAILABLE`, `DEADLINE_EXCEEDED` и `RESOURCE_EXHAUSTED`
|
ожидающих. Для `UNAVAILABLE`, `DEADLINE_EXCEEDED` и `RESOURCE_EXHAUSTED`
|
||||||
каждый RPC допускает один повтор через секунду. Deadline каждой попытки — 15 секунд,
|
обычный RPC допускает один повтор через секунду. Deadline каждой попытки — 15 секунд,
|
||||||
а ожидание места в лимитере не является временем этой сетевой попытки.
|
а ожидание места в лимитере не является временем этой сетевой попытки.
|
||||||
Метаданные используют ту же retry policy. Переполненная очередь завершается
|
Для поиска метаданных `GetInstrumentBy` разрешены две повторные попытки
|
||||||
ошибкой сразу, без повторов admission. Постоянные ошибки и отказ авторизации
|
с паузами 2 и 4 секунды после неудачных попыток. Они применяются к тем же
|
||||||
|
временным кодам и к переполнению локальной очереди; перед каждой попыткой
|
||||||
|
проверяется её вместимость. Постоянные ошибки и отказ авторизации
|
||||||
не повторяются. Подтверждённый `NOT_FOUND` при поиске метаданных означает
|
не повторяются. Подтверждённый `NOT_FOUND` при поиске метаданных означает
|
||||||
отсутствие инструмента, а не временный сбой.
|
отсутствие инструмента, а не временный сбой. Остальные RPC при полной
|
||||||
|
очереди завершаются сразу.
|
||||||
Параметры попыток принадлежат
|
Параметры попыток принадлежат
|
||||||
[T-Bank client](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/integrations/tbank/tbank.client.ts) (справочный источник);
|
[T-Bank client](../../backend/src/integrations/tbank/tbank.client.ts) (workspace-ссылка);
|
||||||
решение об ограниченных повторах — [ADR 005](../adr/005-tbank-upstream-resilience.md).
|
решение об ограниченных повторах — [ADR 005](../adr/005-tbank-upstream-resilience.md).
|
||||||
|
|
||||||
Большое число ранее неизвестных инструментов требует отдельных запросов
|
Большое число ранее неизвестных инструментов требует отдельных запросов
|
||||||
metadata. При перегруженной очереди необязательные имена могут быть заменены
|
metadata. При перегруженной очереди необязательные имена могут быть заменены
|
||||||
идентификаторами; это не доказывает потерю самой позиции. Изменение локального
|
идентификаторами; это не доказывает потерю самой позиции. Изменение локального
|
||||||
лимита требует учёта разрешённой квоты T-Bank: оно не увеличивает квоту
|
лимита требует учёта разрешённой квоты T-Bank: оно не увеличивает квоту
|
||||||
поставщика.
|
поставщика. Диагностика соединения описана в
|
||||||
|
[локальном окружении](../development/local-workspace.md).
|
||||||
|
|
||||||
Ключ шифрования не ротируется автоматически. Новый ключ не расшифрует
|
Ключ шифрования не ротируется автоматически. Лимитер и кэши принадлежат
|
||||||
прежнее подключение: нужен исходный ключ либо повторный ввод собственного
|
|
||||||
token через настройки. Правила [резервного копирования](../architecture/containers.md#сохранение-и-восстановление-данных)
|
|
||||||
учитывают отдельное хранение ключа. Лимитер и кэши принадлежат
|
|
||||||
одному экземпляру; изменения подключения не координируются между
|
одному экземпляру; изменения подключения не координируются между
|
||||||
распределёнными экземплярами backend. Интеграция использует сохранённый
|
распределёнными экземплярами backend. Интеграция использует сохранённый
|
||||||
protobuf-контракт T-Bank: обновление внешнего протокола и проверка
|
protobuf-контракт T-Bank: обновление внешнего протокола и проверка
|
||||||
|
|||||||
+1
-1
@@ -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): рынок, личная позиция, история и события |
|
||||||
|
|
||||||
## Как связаны представления
|
## Как связаны представления
|
||||||
|
|
||||||
|
|||||||
Generated
+303
-3182
File diff suppressed because it is too large
Load Diff
+6
-27
@@ -1,37 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "wealth-ledger-docs",
|
"name": "wealth-ledger-docs",
|
||||||
"version": "0.1.6",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "24.22.1"
|
"node": "24.x"
|
||||||
},
|
},
|
||||||
"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:prepare": "release-it --ci",
|
|
||||||
"commitlint": "commitlint"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "21.2.4",
|
"markdownlint-cli2": "0.23.2",
|
||||||
"@commitlint/config-conventional": "21.2.4",
|
"markdownlint-rule-relative-links": "5.1.2"
|
||||||
"@release-it/conventional-changelog": "12.0.2",
|
|
||||||
"conventional-changelog-conventionalcommits": "10.4.1",
|
|
||||||
"markdownlint-cli": "0.49.1",
|
|
||||||
"markdownlint-rule-relative-links": "5.1.4",
|
|
||||||
"release-it": "21.1.1"
|
|
||||||
},
|
|
||||||
"overrides": {
|
|
||||||
"fast-uri": "3.1.9",
|
|
||||||
"katex": "0.19.1",
|
|
||||||
"markdownlint-cli@0.49.1": {
|
|
||||||
"js-yaml": "5.4.2",
|
|
||||||
"smol-toml": "1.9.1"
|
|
||||||
},
|
|
||||||
"release-it": {
|
|
||||||
"basic-ftp": "6.2.3",
|
|
||||||
"undici": "7.30.1"
|
|
||||||
},
|
|
||||||
"markdown-it": "15.0.3"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-29
@@ -1,31 +1,3 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
||||||
"extends": [
|
|
||||||
"config:recommended",
|
|
||||||
":semanticCommitTypeAll(chore)"
|
|
||||||
],
|
|
||||||
"timezone": "Europe/Moscow",
|
|
||||||
"labels": [
|
|
||||||
"dependencies"
|
|
||||||
],
|
|
||||||
"dependencyDashboard": true,
|
|
||||||
"rangeStrategy": "pin",
|
|
||||||
"packageRules": [
|
|
||||||
{
|
|
||||||
"matchUpdateTypes": [
|
|
||||||
"minor",
|
|
||||||
"patch"
|
|
||||||
],
|
|
||||||
"groupName": "non-major dependencies"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"matchUpdateTypes": [
|
|
||||||
"major"
|
|
||||||
],
|
|
||||||
"dependencyDashboardApproval": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"baseBranchPatterns": [
|
|
||||||
"develop"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
const assert = require("node:assert/strict");
|
|
||||||
const { execFileSync } = require("node:child_process");
|
|
||||||
const { version } = require("../package.json");
|
|
||||||
const git = (...args) => execFileSync("git", args, { encoding: "utf8" }).trim();
|
|
||||||
|
|
||||||
assert.equal(git("status", "--porcelain"), "", "Commit all changes before preparing a release");
|
|
||||||
const tags = git("tag", "--merged", "HEAD", "--list", "v[0-9]*", "--sort=-version:refname")
|
|
||||||
.split("\n").filter(tag => /^v\d+\.\d+\.\d+$/.test(tag));
|
|
||||||
assert.equal(version, tags[0]?.slice(1) ?? "0.0.0",
|
|
||||||
"Version is already prepared or release tags are missing. Fetch tags; finish the existing release before preparing another.");
|
|
||||||
Reference in New Issue
Block a user