Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a9b0409cc | ||
|
|
256d49b045 | ||
|
|
9477e688d1 | ||
|
|
0ee54ca952 | ||
|
|
6fa62fb8b5 |
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": ".markdownlint.json",
|
||||
"MD024": {
|
||||
"siblings_only": true
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"MD013": false
|
||||
},
|
||||
"customRules": [
|
||||
"markdownlint-rule-relative-links"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"filter": [
|
||||
"CHANGELOG.md"
|
||||
],
|
||||
"config": {
|
||||
"MD024": {
|
||||
"siblings_only": true
|
||||
}
|
||||
},
|
||||
"combine": "merge"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('markdownlint-rule-relative-links').default;
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"MD013": false
|
||||
}
|
||||
@@ -1,5 +1,17 @@
|
||||
# Changelog
|
||||
|
||||
## [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
|
||||
|
||||
+4
-3
@@ -177,9 +177,10 @@ Markdown-only правки не требуют тестов приложения
|
||||
|
||||
`security.yml` при push, PR и ежедневно проверяет всю Git-историю через
|
||||
Gitleaks 8.30.1 с redacted-выводом и все npm dependencies через
|
||||
`npm audit --include=dev --audit-level=low`. Override `smol-toml` исправляет
|
||||
DoS в TOML parser markdownlint-cli2; его можно удалить, когда родительский
|
||||
пакет примет исправленную версию.
|
||||
`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 включаются
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
**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
|
||||
|
||||
Два одновременных запроса могут предъявить один действительный refresh token. Отзыв Session для проигравшего запроса делает недействительной сессию успешного запроса, а сохранение отдельной истории token или grace-window для различения replay добавляет непропорциональное состояние локальному MVP.
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
**Status:** Accepted — архитектурное направление согласовано.
|
||||
|
||||
Prefetch policy ниже заменена [ADR 018](018-minimal-runtime-coordination.md):
|
||||
свечи загружаются по запрошенному диапазону, без предварительной загрузки
|
||||
всей истории бумаг портфеля. Остальные гарантии локального хранения сохранены.
|
||||
|
||||
Реализация `persist-tbank-data` добавлена в рабочие ветки приложения;
|
||||
performance/browser и canonical application gates пройдены. Это не отметка
|
||||
развёртывания в production. Ниже сохранён контекст
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# 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.
|
||||
@@ -22,3 +22,4 @@
|
||||
- [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)
|
||||
|
||||
@@ -195,9 +195,9 @@ access token и вращаемый refresh token. [Политика query-кэш
|
||||
|
||||
Авторизация доступа, недопущение token в публичный ответ и атомарность
|
||||
ротации сессии — отдельные свойства; наличие типизированного клиента
|
||||
не доказывает их. Известные расхождения регистрации и деталей валидации
|
||||
отмечены в [аутентификации](../domains/authentication/overview.md#регистрация-и-вход)
|
||||
и [HTTP API](http-api.md#error-contract).
|
||||
не доказывает их. Известное расхождение регистрации отмечено в
|
||||
[аутентификации](../domains/authentication/overview.md#регистрация-и-вход),
|
||||
а действующий формат ошибок описан в [HTTP API](http-api.md#error-contract).
|
||||
|
||||
Поддерживается локальный Compose и ограниченная single-server topology с SQLite.
|
||||
Origin браузера, адрес API и cookie policy должны быть согласованы.
|
||||
|
||||
@@ -43,6 +43,10 @@ Status GET и data GET с `sync=observe` не запускают работу.
|
||||
наблюдение прекращается. Поздний ответ не заменяет уже принятый новый.
|
||||
[HTTP API](../http-api.md) объясняет envelope и экранный refresh.
|
||||
|
||||
Отменой query lifecycle владеет TanStack Query. Status не пересобирает
|
||||
финансовые ответы и не передаёт hash notifications. Свечи нового диапазона
|
||||
загружаются при его открытии; предзагрузка всей истории портфеля не выполняется.
|
||||
|
||||
## Смена подключения
|
||||
|
||||
Каждое чтение проверяет локальную сессию, текущее подключение, известный отказ
|
||||
|
||||
@@ -142,20 +142,15 @@ backups находятся рядом с data directory, вне checkout, и а
|
||||
не удаляются. Незавершённая копия помечается `.partial-` и не подходит для restore.
|
||||
Ключ шифрования хранится отдельно. Локальная копия не защищает от потери диска.
|
||||
|
||||
## Независимое обновление frontend
|
||||
## Единый цикл обновления
|
||||
|
||||
Если изменились только image или конфигурация frontend, backend не
|
||||
пересоздаётся, backup и migration не выполняются. Возможен короткий перерыв
|
||||
на внешней точке входа; старым browser tabs может потребоваться reload.
|
||||
При неизменных исправных контейнерах повтор команды не пересоздаёт сервисы.
|
||||
|
||||
## Независимое обновление backend
|
||||
|
||||
Изменение image или конфигурации backend запускает stop → backup → migration →
|
||||
readiness. Неизменный frontend не пересоздаётся. Во время простоя возможен 502.
|
||||
Каждое обновление, включая повтор того же тега и изменение только frontend,
|
||||
выполняет stop → backup → migration → readiness. Сравнение hashes контейнеров
|
||||
и конфигурации не выполняется. Приняты короткий простой API и дополнительный
|
||||
backup; во время простоя возможен 502, вкладкам может потребоваться reload.
|
||||
Ошибка migration блокирует старт, ошибка готовности останавливает новый backend;
|
||||
ошибка frontend не останавливает исправный backend. Успех фиксируется только
|
||||
после проверок запуска затронутых компонентов.
|
||||
после проверок запуска обоих компонентов.
|
||||
|
||||
Одна file lock защищает установку от параллельных команд. Маркер незавершённого
|
||||
обновления блокирует повтор до разбора оператором. Он содержит этап, версии
|
||||
|
||||
+12
-12
@@ -26,8 +26,8 @@ T-Bank data GET возвращают envelope `{ data, meta }`. `data` може
|
||||
при missing или неполном финансовом агрегате; partial ledger/candles могут
|
||||
содержать подтверждённые строки. `meta` разделяет availability, freshness,
|
||||
lastSuccessfulAt, sync, coverage и состояния зависимостей. `dataRevision`
|
||||
описывает набор данных, `stateVersion` — наблюдаемое состояние, включая
|
||||
пустое завершённое покрытие. Ни одно поле не раскрывает connection revision.
|
||||
описывает набор данных; пустое завершённое покрытие явно представлено в coverage.
|
||||
Публичной hash-версии состояния нет. Ни одно поле не раскрывает connection revision.
|
||||
|
||||
Обычный GET планирует missing/stale зависимости после проверки доступа.
|
||||
GET с `sync=observe` только читает. `POST /api/v1/tbank-sync` принимает
|
||||
@@ -35,6 +35,11 @@ GET с `sync=observe` только читает. `POST /api/v1/tbank-sync` пр
|
||||
scope. `GET /api/v1/tbank-sync/:id` наблюдает разрешённую работу; чужой или
|
||||
прежний handle возвращает `404`. Приём работы не означает её успех.
|
||||
|
||||
Status возвращает state/errorCode без данных ресурсов и численных progress
|
||||
counters. Клиент перечитывает показанные resources через `sync=observe` во время
|
||||
работы и после завершения, сохраняя generated query identity. Sync states ресурса:
|
||||
`idle`, `running`, `failed`; отдельного публичного `queued` нет.
|
||||
|
||||
Source failure принятой работы передаётся безопасным `meta.sync.errorCode`;
|
||||
HTTP transport/access/input errors сохраняют Problem Details. Старые данные
|
||||
при transient failure сохраняют исходное успешное время. Детали поведения —
|
||||
@@ -65,16 +70,11 @@ message/stack. Типичный ответ:
|
||||
|
||||
Для input validation контракт требует `400`, `VALIDATION_ERROR` и `errors` как
|
||||
массив `{ pointer, detail }`, где pointer адресует поле запроса. Это требование
|
||||
не считается проверенным одним описанием OpenAPI: нужна проверка реального
|
||||
HTTP-ответа после всех стадий обработки запроса.
|
||||
|
||||
**Известное расхождение:** согласованная
|
||||
[спецификация аутентификации](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/user-authentication/spec.md#requirement-стандартные-ответы-об-ошибках)
|
||||
требует `pointer` и `detail` для невалидных полей. В текущей конфигурации
|
||||
валидация формирует строковые сообщения, а фильтр ожидает структурированные
|
||||
ошибки полей; поэтому корректность деталей `errors` не подтверждена.
|
||||
Исправление требует отдельного изменения реализации и проверки полного
|
||||
HTTP-потока. OpenAPI сам по себе этого расхождения не устраняет.
|
||||
проверяется реальным HTTP-ответом после всех стадий обработки запроса. Глобальная
|
||||
валидация передаёт фильтру структурированные ошибки без исходных значений и DTO,
|
||||
а фильтр формирует JSON Pointer, включая вложенные поля. HTTP suites подтверждают
|
||||
детали ошибок регистрации, банковской карты и синхронизации; OpenAPI отдельно
|
||||
фиксирует форму ответа.
|
||||
|
||||
Ответ не включает автоматически все поля RFC (`type`, `title`, `instance`).
|
||||
Добавление обязательного поля является изменением публичного контракта.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -55,15 +55,16 @@ sequenceDiagram
|
||||
B-->>F: Access token, профиль, новая refresh-cookie
|
||||
F->>B: Protected request с Bearer
|
||||
else Cookie нет или refresh отклонён
|
||||
B-->>F: 401 и очистка cookie
|
||||
B-->>F: 401 без изменения cookie
|
||||
Note over F: Готовое неавторизованное состояние
|
||||
end
|
||||
```
|
||||
|
||||
В одном экземпляре frontend одновременные refresh используют один
|
||||
незавершённый запрос. Между вкладками используется Web Locks, если браузер
|
||||
его поддерживает. Backend всё равно защищает ротацию атомарно: координация
|
||||
браузеров не заменяет серверную проверку.
|
||||
В одной вкладке одновременные refresh используют один незавершённый запрос;
|
||||
cookie-writing requests упорядочены локально. Межвкладочная синхронизация
|
||||
не поддерживается: после смены сессии в другой вкладке допустимы reload/re-login.
|
||||
Backend защищает ротацию атомарно. Неуспешный refresh не удаляет cookie
|
||||
победившего запроса; недействительный token по-прежнему не восстанавливает сессию.
|
||||
|
||||
Обычный защищённый запрос с `401 UNAUTHORIZED` проходит refresh и один повтор
|
||||
через общий транспорт; auth endpoints из этого цикла исключены. Само
|
||||
|
||||
@@ -91,13 +91,11 @@ Selected, previous и yearAgo имеют независимую готовнос
|
||||
серверным правилом; клиент воспринимает ключ как непрозрачную строку.
|
||||
Это поле принадлежит ответу аналитики; общий журнал операций его не требует.
|
||||
|
||||
Frontend проверяет принадлежность выплат по полученному ключу до подготовки
|
||||
плоского списка выплат. Отсутствующий, пустой или
|
||||
нестроковый ключ, неоднозначные ключи инструментов, неизвестная группа, другая
|
||||
валюта или несовпадение числа выплат приводят к существующему локальному
|
||||
состоянию ошибки аналитики с повторной попыткой. Частичный список выплат не показывается как
|
||||
полный; формула идентификации в клиенте не восстанавливается. Полностью пустой
|
||||
ответ допустим. Состав и исходный серверный порядок записей и знак сумм
|
||||
Backend обеспечивает принадлежность выплат, валюту и receipt count при
|
||||
построении entries и instrument groups из одного набора данных. Frontend
|
||||
доверяет типизированному contract, не восстанавливает формулу идентификации
|
||||
и не выполняет второй consistency validator. Полностью пустой ответ допустим.
|
||||
Состав и исходный серверный порядок записей и знак сумм
|
||||
сохраняются, frontend не пересчитывает денежные итоги. Стабильный ID в пределах подключения и счёта определяет одну операцию:
|
||||
повторная сверка обновляет её без двойного учёта. Одинаковые ID разных счетов
|
||||
остаются разными выплатами. Изменение источника логотипа позволяет заново
|
||||
|
||||
@@ -130,15 +130,12 @@ Backend хранит token в шифротексте AES-256-GCM с новым n
|
||||
[validateEnv](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/config/env.validation.ts) (справочный источник).
|
||||
Общий FIFO-лимитер ограничивает поток RPC и не допускает более 50
|
||||
ожидающих. Для `UNAVAILABLE`, `DEADLINE_EXCEEDED` и `RESOURCE_EXHAUSTED`
|
||||
обычный RPC допускает один повтор через секунду. Deadline каждой попытки — 15 секунд,
|
||||
каждый RPC допускает один повтор через секунду. Deadline каждой попытки — 15 секунд,
|
||||
а ожидание места в лимитере не является временем этой сетевой попытки.
|
||||
Для поиска метаданных `GetInstrumentBy` разрешены две повторные попытки
|
||||
с паузами 2 и 4 секунды после неудачных попыток. Они применяются к тем же
|
||||
временным кодам и к переполнению локальной очереди; перед каждой попыткой
|
||||
проверяется её вместимость. Постоянные ошибки и отказ авторизации
|
||||
Метаданные используют ту же retry policy. Переполненная очередь завершается
|
||||
ошибкой сразу, без повторов admission. Постоянные ошибки и отказ авторизации
|
||||
не повторяются. Подтверждённый `NOT_FOUND` при поиске метаданных означает
|
||||
отсутствие инструмента, а не временный сбой. Остальные RPC при полной
|
||||
очереди завершаются сразу.
|
||||
отсутствие инструмента, а не временный сбой.
|
||||
Параметры попыток принадлежат
|
||||
[T-Bank client](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/integrations/tbank/tbank.client.ts) (справочный источник);
|
||||
решение об ограниченных повторах — [ADR 005](../adr/005-tbank-upstream-resilience.md).
|
||||
|
||||
Generated
+339
-485
File diff suppressed because it is too large
Load Diff
+12
-6
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "wealth-ledger-docs",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": "24.21.0"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "markdownlint-cli2 \"**/*.md\" \"#node_modules\"",
|
||||
"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",
|
||||
"verify": "npm run lint && git diff --check",
|
||||
"release:preview": "release-it --dry-run --ci",
|
||||
"release:prepare": "release-it --ci",
|
||||
@@ -17,13 +17,19 @@
|
||||
"@commitlint/config-conventional": "21.2.2",
|
||||
"@release-it/conventional-changelog": "12.0.0",
|
||||
"conventional-changelog-conventionalcommits": "10.4.0",
|
||||
"markdownlint-cli2": "0.23.2",
|
||||
"markdownlint-rule-relative-links": "5.1.2",
|
||||
"markdownlint-cli": "0.49.1",
|
||||
"markdownlint-rule-relative-links": "5.1.3",
|
||||
"release-it": "21.0.2"
|
||||
},
|
||||
"overrides": {
|
||||
"markdownlint-cli2@0.23.2": {
|
||||
"smol-toml": "1.8.0"
|
||||
"katex": "0.19.0",
|
||||
"markdownlint-cli@0.49.1": {
|
||||
"js-yaml": "5.4.2",
|
||||
"smol-toml": "1.9.0"
|
||||
},
|
||||
"release-it": {
|
||||
"basic-ftp": "6.2.1",
|
||||
"undici": "7.29.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user