diff --git a/.gitea/ISSUE_TEMPLATE/bug.md b/.gitea/ISSUE_TEMPLATE/bug.md new file mode 100644 index 0000000..ef84f40 --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/bug.md @@ -0,0 +1,15 @@ +--- +name: Bug Report +about: Неверное поведение +--- +# Bug Report + +## Проблема и шаги воспроизведения + +## Факт / ожидание + +## Acceptance Criteria + +- [ ] + +## Окружение / ссылки (если применимо) diff --git a/.gitea/ISSUE_TEMPLATE/engineering.md b/.gitea/ISSUE_TEMPLATE/engineering.md new file mode 100644 index 0000000..b443763 --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/engineering.md @@ -0,0 +1,13 @@ +--- +name: Engineering Task +about: CI, тесты, документация, процесс и прочее +--- +# Engineering Task + +## Контекст и результат + +## Scope / связанные Issues (если есть) + +## Acceptance Criteria + +- [ ] diff --git a/.gitea/ISSUE_TEMPLATE/feature.md b/.gitea/ISSUE_TEMPLATE/feature.md new file mode 100644 index 0000000..07fc8f1 --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/feature.md @@ -0,0 +1,15 @@ +--- +name: Feature Request +about: Новое наблюдаемое поведение +--- +# Feature Request + +## Зачем и какой результат + +## Ожидаемое поведение + +## Acceptance Criteria + +- [ ] + +## Зависимости / связанные Issues (если есть) diff --git a/.gitea/ISSUE_TEMPLATE/refactor.md b/.gitea/ISSUE_TEMPLATE/refactor.md new file mode 100644 index 0000000..f128dc1 --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/refactor.md @@ -0,0 +1,15 @@ +--- +name: Refactoring / Technical Debt +about: Улучшение без нового поведения +--- +# Refactoring / Technical Debt + +## Проблема и результат + +## Сохраняемые инварианты / границы + +## Acceptance Criteria + +- [ ] + +## Зависимости / ссылки (если есть) diff --git a/.gitea/PULL_REQUEST_TEMPLATE.md b/.gitea/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..b749661 --- /dev/null +++ b/.gitea/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,19 @@ +# Pull Request + +## Summary + +Краткий результат работы описывается здесь. + +## Linked Issue + +Refs `` (заменить ссылкой на реальную Issue). + +## Changes + +Затронутые части и изменения. + +## Verification + +- Команда / результат / ограничения: + +## Breaking Changes (если есть) diff --git a/.gitea/workflows/commits.yml b/.gitea/workflows/commits.yml index 2372d2c..0fd4494 100644 --- a/.gitea/workflows/commits.yml +++ b/.gitea/workflows/commits.yml @@ -2,7 +2,11 @@ name: Conventional commits on: pull_request: - types: [opened, synchronize, reopened, edited] + types: [opened, synchronize, reopened] + +concurrency: + group: docs-commits-${{ gitea.event.pull_request.number }} + cancel-in-progress: true jobs: commits: @@ -23,4 +27,8 @@ jobs: HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: | printf '%s\n' "$PR_TITLE" | npm run commitlint -- --strict - npm run commitlint -- --strict --from "$BASE_SHA" --to "$HEAD_SHA" + 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 diff --git a/.gitea/workflows/security.yml b/.gitea/workflows/security.yml index 876617f..633d952 100644 --- a/.gitea/workflows/security.yml +++ b/.gitea/workflows/security.yml @@ -2,12 +2,16 @@ name: Docs security on: push: - branches: - - '**' + 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 diff --git a/.gitea/workflows/verify.yml b/.gitea/workflows/verify.yml index 9981701..defd378 100644 --- a/.gitea/workflows/verify.yml +++ b/.gitea/workflows/verify.yml @@ -2,8 +2,13 @@ name: Docs verify on: push: - branches: ['**'] + 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: diff --git a/AGENTS.md b/AGENTS.md index d16b722..b960170 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,6 +3,9 @@ Перед изменением прочитай [README](README.md) и [CONTRIBUTING](CONTRIBUTING.md), затем только затронутую тему. Docs объясняет систему; требования принадлежат основным спецификациям, реализация и 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; внешние источники используй как справочные свидетельства, когда они доступны. @@ -20,7 +23,8 @@ ## Ветки и релизы -Работа — в отдельной ветке от `develop`, затем PR в `develop`; в `main` напрямую +Работа — в issue-linked ветке от `develop`, затем PR в `develop`; в `main` напрямую не пушить. Release/hotfix и синхронизация веток — по [README](README.md#релизы). -Commits и заголовки PR — Conventional Commits; для спецификаций допустим `spec:`. +Commits и заголовки PR — Conventional Commits со ссылкой на Issue (кроме release); +для спецификаций допустим `spec:`. Перед подготовкой релиза прочитай местный README; версии вручную не повышать. diff --git a/CHANGELOG.md b/CHANGELOG.md index 0defadd..1e73dd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## [0.1.4](https://git.personal.ksv741.ru/WealthLedger/docs/compare/v0.1.3...v0.1.4) (2026-10-12) + +### Bug Fixes + +* align docs commitlint workflow (WealthLedger/main[#27](https://git.personal.ksv741.ru/WealthLedger/docs/issues/27)) ([602eb43](https://git.personal.ksv741.ru/WealthLedger/docs/commit/602eb43294509274cbd9003be31f726b6e275a1e)) + +### Documentation + +* clarify card summary totals ([df6f010](https://git.personal.ksv741.ru/WealthLedger/docs/commit/df6f0109ed696a529cf2622e23fceeab8133f932)) +* document hourly attempt lifecycle semantics (WealthLedger/backend[#56](https://git.personal.ksv741.ru/WealthLedger/docs/issues/56)) ([1378ac9](https://git.personal.ksv741.ru/WealthLedger/docs/commit/1378ac9fe2fcfd43bd71421e62b02f00069be928)) +* document hourly investment reads ([f0cab44](https://git.personal.ksv741.ru/WealthLedger/docs/commit/f0cab445d7e7076bf92a61f1023b8bf51e094d33)) +* document hourly sync history ([e0bceea](https://git.personal.ksv741.ru/WealthLedger/docs/commit/e0bceea936239c08f4717e264be23d7f60552398)) +* document stock screener ([2c34023](https://git.personal.ksv741.ru/WealthLedger/docs/commit/2c34023af8593f305f7c81e672b4e179c0992cf3)) +* pin docs workflow reference (WealthLedger/main[#27](https://git.personal.ksv741.ru/WealthLedger/docs/issues/27)) ([c24e4b9](https://git.personal.ksv741.ru/WealthLedger/docs/commit/c24e4b990aaeffd02e05f7de21942579e452cecf)) + +### CI + +* avoid duplicate PR checks ([4caa5e9](https://git.personal.ksv741.ru/WealthLedger/docs/commit/4caa5e92b7d2597e5c7725b2036007b71ece2abd)) +* skip checks on PR metadata edits ([e6fd4f6](https://git.personal.ksv741.ru/WealthLedger/docs/commit/e6fd4f64b81dd73b33959b845a1babcfac1891c8)) + +### Maintenance + +* align docs engineering workflow (WealthLedger/main[#27](https://git.personal.ksv741.ru/WealthLedger/docs/issues/27)) ([eaf7ede](https://git.personal.ksv741.ru/WealthLedger/docs/commit/eaf7ede8414f0df972f9ae7ab9e223a6350b5462)) + ## [0.1.3](https://git.personal.ksv741.ru/WealthLedger/docs/compare/v0.1.2...v0.1.3) (2026-10-07) ### Bug Fixes diff --git a/README.md b/README.md index c48ab8c..4be690d 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ WealthLedger помогает частному инвестору понимат | [Операции](domains/operations/overview.md) | Исполненные операции, фильтры и пагинация | | [Доход](domains/income/overview.md) | Полученные купоны и дивиденды, периоды и состав расчёта | | [Будущие выплаты](domains/income/upcoming-events.md) | Календарь по текущим позициям и ограничения прогноза | -| [Акции](domains/instruments/stocks.md) и [облигации](domains/instruments/bonds.md) | Поиск выпуска, рыночные показатели и личная история | +| [Скринер акций](domains/instruments/stocks.md) и [облигации](domains/instruments/bonds.md) | Подбор акций по параметрам, поиск выпуска, рыночные показатели и личная история | ## Найти материал по задаче @@ -72,9 +72,11 @@ WealthLedger помогает частному инвестору понимат ## Релизы Репозиторий выпускается независимо. `develop` — интеграция, `main` — принятые -релизы. Работа: `codex/` от актуального `develop`, PR в `develop`. +релизы. Обычная работа: issue-linked ветка от актуального `develop`, PR в `develop`; +общие правила — в Engineering Workflow по ссылке из AGENTS. Обычные PR допускают squash; release и обратная синхронизация сохраняют историю. -Commits и PR titles — Conventional Commits, включая `spec:`. Версии вручную не повышай. +Commits и PR titles — Conventional Commits со ссылкой на Issue (кроме release), +включая `spec:`. Версии вручную не повышай. 1. Получи `main`, `develop` и теги: `git fetch origin --tags`. Убедись, что `develop` содержит последний релиз. Создай `release/` от проверенного diff --git a/adr/017-local-tbank-reads-and-background-sync.md b/adr/017-local-tbank-reads-and-background-sync.md index 15eef69..e0478ba 100644 --- a/adr/017-local-tbank-reads-and-background-sync.md +++ b/adr/017-local-tbank-reads-and-background-sync.md @@ -1,6 +1,8 @@ # 017. Локальное чтение данных T-Bank и фоновая синхронизация -**Status:** Accepted — архитектурное направление согласовано. +**Status:** Accepted — историческое архитектурное направление. Решения ниже о +request-driven запуске, ручном обновлении и отсутствии периодического обхода +заменены [ADR 019](019-hourly-reads-and-online-instrument-reference.md). Prefetch policy ниже заменена [ADR 018](018-minimal-runtime-coordination.md): свечи загружаются по запрошенному диапазону, без предварительной загрузки diff --git a/adr/019-hourly-reads-and-online-instrument-reference.md b/adr/019-hourly-reads-and-online-instrument-reference.md new file mode 100644 index 0000000..7ec7f9e --- /dev/null +++ b/adr/019-hourly-reads-and-online-instrument-reference.md @@ -0,0 +1,37 @@ +# 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. diff --git a/adr/020-hourly-sync-verification-evidence.md b/adr/020-hourly-sync-verification-evidence.md new file mode 100644 index 0000000..5d3fb7c --- /dev/null +++ b/adr/020-hourly-sync-verification-evidence.md @@ -0,0 +1,42 @@ +# 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. diff --git a/adr/README.md b/adr/README.md index efe8b86..2d1e314 100644 --- a/adr/README.md +++ b/adr/README.md @@ -23,3 +23,5 @@ - [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) diff --git a/architecture/cross-cutting/data-freshness.md b/architecture/cross-cutting/data-freshness.md index 27da9b0..905cfbc 100644 --- a/architecture/cross-cutting/data-freshness.md +++ b/architecture/cross-cutting/data-freshness.md @@ -1,51 +1,72 @@ # Свежесть данных и повторное использование -WealthLedger читает инвестиционные данные из SQLite. Обычный GET возвращает -сохранённый результат и запрашивает фоновые зависимости, если они отсутствуют -или устарели. HTTP-ответ не ждёт T-Bank. Модель принята в -[ADR 017](../../adr/017-local-tbank-reads-and-background-sync.md). +WealthLedger читает личные инвестиционные данные из SQLite. Обычный GET и его +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) — успешный ответ в TanStack Query. Он сохраняет показанное содержимое при временной ошибке -запроса в той же границе доступа. SQLite сохраняет источники независимо от -браузера: membership, последний полный portfolio, исполненные операции, -общие сведения об инструментах, котировки, свечи и календари. +HTTP-перечитывания в той же границе доступа. Это не серверный fallback и не +состояние часового обхода. SQLite независимо от браузера хранит membership, +последний полный portfolio, исполненные операции, прогнозы, общие сведения об +инструментах, котировки, свечи и календари. [Полнота периода](../../glossary.md#полнота-истории-счёта) и свежесть различаются. -Успешно проверенный пустой интервал полон; последняя страница журнала сама -по себе полноту не доказывает. Ошибка сверки сохраняет прежние данные и время. -Старый полный портфель не удаляется по истечении TTL. `isStale` показывает -возраст portfolio source, а не сам факт неудачного обновления. +Успешно проверенный пустой результат полон и доступен; последняя страница +журнала сама по себе полноту не доказывает. Результат каждого личного ресурса +в `{ data, meta }` описывают пять независимых значений: freshness, +completeness, `lastSuccessfulAt`, `lastAttempt` и availability. Первые, +полнота и `lastSuccessfulAt` относятся только к возвращённому подтверждённому +результату. `lastAttempt` описывает относящуюся к ресурсу часовую попытку: +`none`, `running`, `succeeded`, `partial` или `failed`. -| Источник | Когда обычный запрос требует обновления | +Итог попытки выводится из фактических результатов её шагов: попытка, не +завершившая обязательный шаг, не обозначается полным успехом, а полученные +успешные результаты сохраняются. Ожидание каждого шага ограничено; зависший +шаг учитывается проблемным, а начатое обращение к T-Bank не отменяется. +Попытка, потерявшая выполнение после сбоя сохранения или остановки процесса, +закрывается частичным успехом либо неуспехом и не блокирует следующий обход +дольше обычного часового срока. + +Поздняя неуспешная или частичная попытка сохраняет прежний полный результат и +его исходное время. Если полного результата нет, `data` равно `null`: состояние +availability объясняет первоначальную загрузку, временную недоступность либо +недоступность подключения. Пустой массив не означает отсутствие результата. +Старый полный portfolio не удаляется из-за возраста. `Portfolio.isStale` +сохраняет смысл возраста его snapshot, а не обозначает неуспех обхода или +клиентскую ошибку перечитывания. + +| Источник | Владелец нормального обновления | | --- | --- | -| Portfolio, котировки, изменяемый хвост дневных свечей | Через 60 секунд | -| Membership и изменяемый хвост операций | Через 5 минут | -| Metadata, поиск каталога, company и календари | Через сутки | -| Полные прошлые интервалы операций и завершённые свечи | При явном обновлении показанного периода | +| Membership, portfolio, изменяемый хвост операций и события | Часовой обход текущего подключения | +| Котировки и изменяемый хвост дневных свечей | Установленная политика источника; не ordinary personal GET | +| Metadata, поиск каталога и company | Онлайн при чтении | +| Полные прошлые интервалы операций и завершённые свечи | Не обновляются часовым обходом или обычным GET | -TTL не создаёт таймер импорта. Запрошенная работа может закончиться после -ухода со страницы. После перезапуска нет автоматического обхода intent: -следующий подходящий запрос повторяет незавершённый интервал с первой страницы, -переиспользуя полные интервалы. Cursor брокера не служит durable checkpoint. +Два исключения разрешают только первое получение отсутствующего результата: +первое открытие неподтверждённого исторического интервала операций получает +только этот интервал и возвращает локальные строки с неполным покрытием; первое +открытие диапазона свечей без подтверждённого локального результата получает +только этот диапазон. Ни одно исключение не обновляет membership, portfolio, +события, другой интервал или диапазон. Их HTTP retry также только перечитывает +локальное состояние. Подтверждённый полный интервал или диапазон остаётся +локальным. -Ручное «Обновить» перечитывает зависимости текущего экрана и применённый -период независимо от TTL. Повторная сверка полного периода публикуется одной -транзакцией после всех страниц, интервалов и счетов, включая `all`. -Сбой сохраняет весь прежний результат. Первая загрузка может публиковать -полные малые интервалы и показывать частичный период без окончательных сумм. - -Клиент наблюдает только известную активную работу открытого экрана. -Status GET и data GET с `sync=observe` не запускают работу. После изменения -состояния ответ попадает в ту же query identity; после ухода или завершения -наблюдение прекращается. Поздний ответ не заменяет уже принятый новый. -[HTTP API](../http-api.md) объясняет envelope и экранный refresh. - -Отменой query lifecycle владеет TanStack Query. Status не пересобирает -финансовые ответы и не передаёт hash notifications. Свечи нового диапазона -загружаются при его открытии; предзагрузка всей истории портфеля не выполняется. +Часовой обход сохраняет успешные разделы независимо, а failed section не +заменяет прежний complete result. Отменой query lifecycle владеет TanStack +Query. Открытый экран перечитывает ресурс не чаще одного раза в 30 минут; +это локальный HTTP read. Совместимый параметр `sync=observe`, пока он есть в +маршруте, также является только чтением и не представляет команду обходу. +[HTTP API](../http-api.md) описывает исполняемый контракт envelope. ## Смена подключения diff --git a/architecture/http-api.md b/architecture/http-api.md index 75aa35a..a1c9bf9 100644 --- a/architecture/http-api.md +++ b/architecture/http-api.md @@ -2,7 +2,8 @@ ## Источник контракта -Backend генерирует OpenAPI из собственного публичного API. Frontend использует +Backend генерирует OpenAPI из собственного публичного API. Этот сгенерированный +OpenAPI — исполняемый источник HTTP-контракта. Frontend использует предоставленный OpenAPI snapshot как актуальный вход и локально генерирует типизированный клиент. Backend владеет контрактом, а подготовка и доставка схемы находятся вне frontend. Это разделение принято в @@ -20,29 +21,40 @@ artifact у себя и проверяет соответствие generated ty API имеет prefix `/api/v1`; Swagger публикуется для локальной разработки по environment policy, а не используется frontend в runtime. +## Скринер акций + +`POST /api/v1/stocks/screener/search` всегда создаёт новый результат по +условиям скринера, включая пустой подтверждённый список. Ответ первой страницы +содержит непрозрачный `searchId`, время получения каталога и признак следующей +страницы. `GET /api/v1/stocks/screener/search/{searchId}?page=...` читает +последующую страницу того же результата, не запускает новый поиск и сохраняет +состав и время списка. Идентификатор результата принадлежит текущему пользователю +и его подключению; недоступный или чужой результат возвращает ошибку, а не +пустой список. Точные поля и статусы определяет backend-generated OpenAPI. + ## Инвестиционные данные и синхронизация -T-Bank data GET возвращают envelope `{ data, meta }`. `data` может быть `null` -при missing или неполном финансовом агрегате; partial ledger/candles могут -содержать подтверждённые строки. `meta` разделяет availability, freshness, -lastSuccessfulAt, sync, coverage и состояния зависимостей. `dataRevision` -описывает набор данных; пустое завершённое покрытие явно представлено в coverage. -Публичной hash-версии состояния нет. Ни одно поле не раскрывает connection revision. +Затронутые 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 полем, а не заменой состояния попытки. -Обычный GET планирует missing/stale зависимости после проверки доступа. -GET с `sync=observe` только читает. `POST /api/v1/tbank-sync` принимает -валидированный screen descriptor и возвращает `202` с handle и нормализованным -scope. `GET /api/v1/tbank-sync/:id` наблюдает разрешённую работу; чужой или -прежний handle возвращает `404`. Приём работы не означает её успех. +Только Accounts дополнительно возвращает top-level `sync`: `null`, если у +текущего подключения ещё не было часовой попытки, иначе connection-wide +наблюдаемый результат. Это наблюдение, не команда. Обычный GET и retry после +проверки доступа читают локальное состояние и не начинают личную работу. +Устаревший `sync=observe`, пока он сохранён для совместимости маршрута, также +только читает. Request-started screen-sync command/status не является текущей +AS-IS моделью. -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 сохраняют исходное успешное время. Детали поведения — +Безопасные ошибки завершённой часовой попытки передаются в `lastAttempt` и +Accounts `sync`; HTTP transport/access/input errors сохраняют Problem Details. +Старые данные при временной source failure сохраняют исходное успешное время. +Ни одно поле не раскрывает connection revision. Детали поведения — [свежесть и полнота](cross-cutting/data-freshness.md). Envelope несовместим с прежними bare-array ответами: backend/frontend diff --git a/commitlint.config.cjs b/commitlint.config.cjs index 4aa31e2..512700c 100644 --- a/commitlint.config.cjs +++ b/commitlint.config.cjs @@ -1,4 +1,12 @@ module.exports = { extends: ["@commitlint/config-conventional"], - rules: { "type-enum": [2, "always", ["feat", "fix", "perf", "docs", "spec", "refactor", "build", "ci", "test", "style", "chore", "revert"]] }, + 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"], + }, }; diff --git a/domains/cards/overview.md b/domains/cards/overview.md index 2e8f648..9cc5528 100644 --- a/domains/cards/overview.md +++ b/domains/cards/overview.md @@ -89,9 +89,8 @@ JavaScript Number. Отдельный ввод ограничен signed64: на следующие страницы. Баланс при выборе прошлого периода остаётся текущим. Сводка учитывает все подходящие записи, включая не показанные на странице. -Сводка одной или всех карт показывает одну итоговую сумму расходов с учётом -возвратов и компактные строки категорий по убыванию итоговых расходов. Отдельные показатели «Возвращено» -и «После возвратов» в интерфейсе скрыты; расчёт и операции возврата сохранены. Возврат уменьшает расходы месяца своего +Сводка одной или всех карт отдельно показывает расходы, возвращённую сумму и +итог после возвратов, а также компактные строки категорий по убыванию итоговых расходов. Возврат уменьшает расходы месяца своего получения: августовская покупка 1 000 ₽ и сентябрьский возврат 600 ₽ дают августовский итог 1 000 ₽ и сентябрьский −600 ₽ при отсутствии других трат. Начальный остаток, пополнения и переводы в эти показатели не входят. diff --git a/domains/income/upcoming-events.md b/domains/income/upcoming-events.md index 2f9de3c..4d18cfe 100644 --- a/domains/income/upcoming-events.md +++ b/domains/income/upcoming-events.md @@ -40,10 +40,10 @@ ## Период и список Без фильтра период начинается сегодня в UTC и заканчивается через 30 дней -после начала, обе даты включены. Поля «С» и «По» позволяют изменить период: -«Применить» сохраняет выбранные календарные даты в URL и запрашивает результат, -«Сбросить» возвращает период по умолчанию. Обзор счёта сохраняет прогноз -на текущую дату и следующие 30 дней. +после начала, обе даты включены. `from` и `to` принимают только строгие даты +`YYYY-MM-DD`; без `from` используется текущая дата UTC, без `to` — `from + 30` +календарных дней. Поля «С» и «По» сохраняют выбранные календарные даты в URL, +а «Сбросить» возвращает период по умолчанию. События упорядочены по календарной дате. У каждой строки видны тип, инструмент, дата и доступная сумма в исходной валюте. Список не имеет @@ -52,10 +52,13 @@ отсутствия всех будущих выплат. Инструмент без поддерживаемых данных не получает искусственную строку «нет данных». -API принимает необязательные `from` и `to` в формате `YYYY-MM-DD` для -отдельного и общего счёта. Без `from` используется сегодня в UTC, -без `to` — 30 дней после начала. Неверный формат или несуществующая дата -дают `400`; обратный период — `400 INVALID_EVENTS_PERIOD`. +Сохранённый forecast сохраняет свой `calculatedAt` и исходное включительное +окно. GET для отдельного счёта или `all` возвращает только пересечение этого +окна и запрошенного UTC-периода, не меняя ни `calculatedAt`, ни исходное окно. +Пустое пересечение или отсутствие событий в пересечении — успешный пустой +список с теми же metadata; это не отсутствие forecast. Неверный формат или +несуществующая дата дают `400`; обратный период — `400 INVALID_EVENTS_PERIOD` +без замены периода и без запуска личной работы. Фильтрация на сервере предшествует сортировке и вычислению `incomeTotalsByCurrency`, включающего только купоны и дивиденды. Итоги остаются в ответе API; полный экран показывает список без общего итога. @@ -63,26 +66,30 @@ API принимает необязательные `from` и `to` в форма ## Получение и отказы Запрос требует сессии, подключения и локально подтверждённого membership. -Прогноз строится из личного сохранённого portfolio и общих календарей: T-Bank для -дивидендов, MOEX для bond events. Отдельного синхронного получения portfolio -нет. Личные оценки сохраняются отдельно от общих календарей. +Он читает сохранённый forecast и не запускает личную синхронизацию. Часовой +обход строит forecast из полного личного portfolio и общих календарей: T-Bank +для дивидендов, MOEX для bond events. Личные оценки сохраняются отдельно от +общих календарей. -Последний полный forecast привязан к scope, периоду и membership. -Если новый portfolio требует ещё недоступного календаря, прежний полный -прогноз и его source times остаются, в том числе после рестарта. Новый -полный результат заменяет его атомарно. Без fallback показывается -missing/partial, а не подтверждённое отсутствие выплат. Другой период или -изменившийся membership не получают старый прогноз под новым названием. -Source failure сообщается в metadata и не изменяет успешное время. +Последний полный forecast привязан к scope, исходному окну и membership. Если +часовой обход не может получить полный новый forecast, прежний полный прогноз, +его `calculatedAt` и исходное окно остаются читаемыми, в том числе после +рестарта; `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). Внешние границы объясняют интеграции [T-Bank](../../integrations/tbank.md) diff --git a/domains/instruments/bonds.md b/domains/instruments/bonds.md index 7e030ae..ef47b3e 100644 --- a/domains/instruments/bonds.md +++ b/domains/instruments/bonds.md @@ -143,3 +143,9 @@ BondBy переиспользуется зависимостями выпуск и [проверками](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/bonds/application/bond.spec.ts). Границы источников описаны в [разделе интеграций](../../integrations/README.md), сходное разделение рыночных и личных данных — в [акциях](stocks.md). + +## Online reference + +Bond detail, search, and ticker lookup are read from the upstream integration +on each request. Saved quotes, candles, events, ratings, and portfolio labels +retain their own freshness and failure boundaries. diff --git a/domains/instruments/stocks.md b/domains/instruments/stocks.md index 6fc583f..e058cf6 100644 --- a/domains/instruments/stocks.md +++ b/domains/instruments/stocks.md @@ -28,24 +28,29 @@ на frontend. Общее для акций и облигаций восстановление справочных данных принадлежит [загрузке портфеля на backend](../portfolio/flows/portfolio-loading.md). -Раздел «Акции» в боковой и мобильной навигации ведёт на `/stocks`: поиск -по названию или тикеру через каталог Т-Банка, начиная с двух символов. -Результаты разных выпусков, включая обыкновенные и привилегированные акции, -имеют отдельные UID. Карточка результата показывает логотип, последнюю цену -с её временем, тип бумаги, площадку, код режима, ISIN и доступность торгов через -API. Поиск показывает одну карточку на нормализованный ISIN. До ограничения -в 30 результатов выбирается представитель выпуска: доступность через API, -точное совпадение тикера, затем режим TQBR; равные варианты упорядочены по UID. -Записи без ISIN сохраняются отдельно по UID. Обыкновенные и привилегированные -акции с разными ISIN не объединяются. Цена и ссылка принадлежат выбранному -UID; данные разных режимов не смешиваются. Прямые ссылки из портфеля сохраняют +Раздел «Акции» в боковой и мобильной навигации ведёт на `/stocks`: скринер +запускается кнопкой «Найти» с необязательным названием или тикером, сектором, +типом акции, валютой расчётов, площадкой, страной риска и максимальным размером +лота. Он использует полный полученный каталог и показывает только акции, для +которых каталог подтверждает доступность покупки и торговли через API. Это не +гарантирует, что конкретная заявка пользователя будет исполнена прямо сейчас. + +Скринер показывает одну карточку на выпуск с нормализованным ISIN, а выпуск +без ISIN остаётся отдельной карточкой точного UID. Обыкновенные и +привилегированные акции с разными ISIN не объединяются. После отбора карточки +упорядочены по тикеру и UID, разделены на страницы по 30 и показывают единое +время получения списка. Изменение условий не заменяет текущий список до +следующего нажатия «Найти». Повторный поиск получает новый каталог; страницы +одного результата не пересчитываются. Подтверждённый пустой результат отличается +от ошибки источника, для которой интерфейс предлагает повторить поиск. + +Ссылка карточки принадлежит выбранному UID; данные разных режимов не смешиваются. +Прямые ссылки из портфеля сохраняют исходный UID, даже если поиск выбрал другой режим этого выпуска; такая страница сохраняет UUID в адресе. Если один точный тикер относится к разным выпускам, -сервер возвращает `409 STOCK_TICKER_AMBIGUOUS`, а страница предлагает поиск. -Поиск тикера нечувствителен к регистру, но использует точное совпадение: -SBER не заменяется на SBERP. Однобуквенные тикеры также поддерживаются. +сервер возвращает `409 STOCK_TICKER_AMBIGUOUS`. Прямая ссылка не требует предварительного открытия портфеля. Хлебные крошки -«Акции → название» возвращают к поиску. +«Акции → название» возвращают к скринеру. ## Содержание и состояния @@ -144,10 +149,11 @@ TTM означает последние 12 месяцев, MRQ — послед ## Получение данных и ограничения Общие metadata, discovery, company, quotes, свечи и календари сохраняются -в SQLite. Поиск хранит подтверждённый набор UID для конкретного query; -успешный SBER не подтверждает пустой GAZP. Отсутствующее enrichment не -скрывает найденную бумагу. Сохранённые общие источники переиспользуются -между пользователями, но личные позиции и выплаты всегда проверяют доступ. +в SQLite. Скринер получает каталог online и показывает время его получения; +временная недоступность каталога не подтверждает отсутствие подходящих акций. +Отсутствующее enrichment не скрывает найденную бумагу. Сохранённые общие +источники переиспользуются между пользователями, но личные позиции и выплаты +всегда проверяют доступ. Обычное чтение не ждёт RPC; missing/stale зависимости запрашиваются в фоне через существующий T-Bank limiter. Минутная свежесть применяется к quotes @@ -184,3 +190,10 @@ scope. Частичные строки видны с предупреждени [рыночным данным](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/stocks/application/stock-market.service.ts), [выплатам](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/stocks/application/stock-income.service.ts) и [проверкам](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/stocks/application/stock.spec.ts). Сопоставление с облигациями — на [странице облигаций](bonds.md). + +## Online reference + +Stock detail, company data, search, and ticker lookup are read from the +upstream integration on each request. Saved positions, quotes, candles, and +events remain independent local resources and are not replaced by a reference +read failure. diff --git a/domains/portfolio/flows/portfolio-loading.md b/domains/portfolio/flows/portfolio-loading.md index 5118703..1d31896 100644 --- a/domains/portfolio/flows/portfolio-loading.md +++ b/domains/portfolio/flows/portfolio-loading.md @@ -1,46 +1,47 @@ # Загрузка портфеля -Экран `/accounts/:accountId` читает последний полный snapshot из SQLite -после проверки сессии, подключения и локального membership. Если список -счетов ещё неизвестен, сервер сообщает о загрузке доступа без выдачи личных -данных неизвестного ID. Известный недоступный счёт возвращает `404`. +Экран `/accounts/:accountId` читает последний полный snapshot из SQLite после +проверки сессии, подключения и локального membership. Если список счетов ещё +неизвестен, сервер сообщает о загрузке доступа без выдачи личных данных +неизвестного ID. Известный недоступный счёт возвращает `404`. ## Основной обмен -Обычный GET возвращает локальный envelope. Missing/stale зависимости -запрашиваются в фоне; чтение не ждёт T-Bank. Для нового полного snapshot -нужны оба финансовых ответа `GetPortfolio` и `GetPositions`. Суммы и позиции -публикуются вместе; metadata дополняет представление независимо, с доступным -UID/FIGI вместо имени до её загрузки. +Обычный GET и retry возвращают read-only локальный `{ data, meta }` envelope: +они не ждут и не вызывают T-Bank и не предлагают ручную синхронизацию. Часовой +обход текущего подключения обновляет Portfolio независимо от экрана. Для нового +полного snapshot ему нужны оба финансовых ответа `GetPortfolio` и +`GetPositions`; суммы и позиции публикуются вместе. Metadata инструмента +дополняет представление независимо, с доступным UID/FIGI вместо имени до её +онлайн-загрузки. -Свежесть portfolio — 60 секунд. Истечение срока не удаляет snapshot и не -создаёт таймер. «Обновить» на обзоре перечитывает зависимости текущего экрана: -membership, portfolio, показанные операции, доход и будущие события. -Секции могут быть готовы независимо. Подробнее — -[свежесть](../../../architecture/cross-cutting/data-freshness.md). +Открытый экран выполняет локальное перечитывание не чаще одного раза в +30 минут; retry также повторяет только HTTP GET. `data: null` означает, что +полного snapshot ещё нет, а не нулевой портфель. Пустой подтверждённый portfolio +остаётся available complete result. Секции могут быть готовы независимо. +Подробнее — [свежесть](../../../architecture/cross-cutting/data-freshness.md). ## Границы доступа -Публикация проверяет connection revision и attempt в той же транзакции. -Замена или удаление подключения не допускает позднюю запись прежней работы. -Frontend очищает недоступный personal view и отклоняет запоздалые ответы. -В режиме [«Все счета»](../all-accounts.md#полнота-свежесть-и-доступ) состав -accounts и snapshots читается согласованно, но их source times могут различаться. +Публикация проверяет connection revision и attempt в той же транзакции. Замена +или удаление подключения не допускает позднюю запись прежней работы. Frontend +очищает недоступный personal view и отклоняет запоздалые ответы. В режиме +[«Все счета»](../all-accounts.md#полнота-свежесть-и-доступ) состав accounts и +snapshots читается согласованно, но их source times могут различаться. ## Ошибки и результат для пользователя Нет подключения — `409 TBANK_NOT_CONNECTED`, известный отказ токена — `400 INVALID_TBANK_TOKEN`, недоступный счёт — `404`. Они не маскируются -запасным snapshot. Transient source failure оставляет прежний полный портфель -и его исходное время с безопасным предупреждением. При отсутствии snapshot -показывается missing/failed, без нулевых итогов. `503` означает ошибку -обслуживания HTTP-запроса, а не принятой фоновой работы. +запасным snapshot. Временная ошибка часового обхода оставляет прежний полный +portfolio и его исходное `lastSuccessfulAt`; `meta.lastAttempt` сообщает о +failed или partial попытке отдельно. `Portfolio.isStale` сообщает возраст +snapshot, а Client Cached Snapshot и ошибка браузерного HTTP-перечитывания +имеют независимый смысл. При отсутствии полного snapshot `data: null` и +metadata обозначает initial loading либо временную недоступность, без нулевых +итогов. `503` означает ошибку обслуживания самого HTTP-запроса, а не +завершившуюся фоновую работу. -Observer запрашивает статус известной работы, затем `sync=observe` читает -изменившийся ресурс без запуска синхронизации. При уходе и terminal outcome -наблюдение прекращается. Повторное открытие может запросить недостающее; -после рестарта сохранённый snapshot доступен сразу. - -Денежные правила находятся в [справочнике](../data-reference.md), -проверка расхождения — в [диагностике](../how-to/investigate-discrepancy.md), -RPC/лимиты — в [интеграции T-Bank](../../../integrations/tbank.md). +Денежные правила находятся в [справочнике](../data-reference.md), проверка +расхождения — в [диагностике](../how-to/investigate-discrepancy.md), RPC/лимиты +— в [интеграции T-Bank](../../../integrations/tbank.md). diff --git a/glossary.md b/glossary.md index f4eb653..866c22d 100644 --- a/glossary.md +++ b/glossary.md @@ -128,6 +128,14 @@ Instrument обогащают отображение Position или Event, но _Не использовать_: бумага, тикер, актив позиции. +## Скринер акций + +Подбор выпусков акций по заданным условиям из каталога T-Bank с указанием +времени получения списка. Скринер отличается от текстового поиска конкретного +инструмента, личного портфеля и подтверждения исполнимости сделки. + +_Не использовать_: гарантия покупки, личная проверка доступа к сделке. + ## Instrument Logo Имя файла бренда T-Bank для отображения Instrument в интерфейсе; @@ -152,6 +160,15 @@ _Не использовать_: валютная позиция, заблоки _Не использовать_: история портфеля, snapshot для графика. +## Точка истории стоимости портфеля + +Полная оценка одного Brokerage Account в одной валюте на момент успешного +обновления Portfolio; последовательность таких точек описывает изменение +оценки во времени, включая неторговые дни. Не является Portfolio Snapshot +или инвестиционной доходностью; отсутствие цены позиции не приравнивается к нулю. + +_Не использовать_: снимок состава портфеля, доходность. + ## Client Cached Snapshot Технический термин: последний успешно полученный frontend-ответ React Query, который остаётся diff --git a/integrations/tbank.md b/integrations/tbank.md index f274f81..2214df2 100644 --- a/integrations/tbank.md +++ b/integrations/tbank.md @@ -20,6 +20,12 @@ T-Bank определяет исходные данные и права token. B сессию, выбирает подключение её пользователя и устанавливает доступ к счёту. Наличие идентификатора счёта в URL этого доступа не подтверждает. +Скринер акций получает полный online-каталог акций T-Bank для каждого нового +поиска. В него входят только инструменты, для которых каталог подтверждает и +доступность покупки, и торговли через API. Эти признаки описывают полученный +снимок каталога: они не подтверждают личные права пользователя, состояние торгов +или исполнение будущей заявки. Скринер не использует личный портфель как каталог. + [MOEX](moex.md) и [НКР](nkr.md) дополняют публичные сведения об облигациях; они не заменяют брокера как источник личных позиций и исполненных выплат. Границы источников показаны в [контексте](../architecture/context.md), смысл дополнений — в @@ -91,7 +97,12 @@ sequenceDiagram `INVALID_TBANK_TOKEN` означает отказ внешней аутентификации или прав доступа. `TBANK_UNAVAILABLE` сообщает о временной или иной upstream-ошибке, включая -исчерпание очереди. Ответы и ошибки не содержат token. Удаление подключения +исчерпание очереди. Диагностические причины жизненного цикла часовой попытки +(`INTERRUPTED` — прерывание до нормального завершения, `DEADLINE` — истечение +ограничения ожидания шага) не относятся к ошибкам T-Bank: они остаются +внутренней диагностикой и не раскрываются в hourly-read. Истечение ограничения +ожидания не отменяет уже начатые обращения к T-Bank; их поздние результаты +остаются в строках шагов. Ответы и ошибки не содержат token. Удаление подключения идемпотентно: удаляются локальный секрет, личные datasets и work; поздняя публикация прежней revision запрещена. Общие неперсональные источники могут сохраняться. Оно не описывает отзыв token на стороне T-Bank. diff --git a/overview.md b/overview.md index 479b29d..ede05cb 100644 --- a/overview.md +++ b/overview.md @@ -24,7 +24,7 @@ WealthLedger — локальное приложение для частного | Посчитать полученные выплаты | [Пассивный доход](domains/income/overview.md): исполненные купоны и дивиденды за календарный период | | Посмотреть ожидаемые события | [Будущие выплаты](domains/income/upcoming-events.md): купоны, дивиденды, погашения и оферты по текущим позициям | | Увидеть общий состав инвестиций | Режим [«Все счета»](domains/portfolio/all-accounts.md), сохраняющий принадлежность строк к счетам | -| Изучить конкретный инструмент | Поиск и страницы [акций](domains/instruments/stocks.md) и [облигаций](domains/instruments/bonds.md): рынок, личная позиция, история и события | +| Изучить конкретный инструмент | [Скринер акций](domains/instruments/stocks.md) по параметрам каталога, страницы акций и поиск [облигаций](domains/instruments/bonds.md): рынок, личная позиция, история и события | ## Как связаны представления diff --git a/package-lock.json b/package-lock.json index 5fb0499..2a2afcf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "wealth-ledger-docs", - "version": "0.1.3", + "version": "0.1.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "wealth-ledger-docs", - "version": "0.1.3", + "version": "0.1.4", "devDependencies": { "@commitlint/cli": "21.2.2", "@commitlint/config-conventional": "21.2.2", diff --git a/package.json b/package.json index 780841f..0002403 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wealth-ledger-docs", - "version": "0.1.3", + "version": "0.1.4", "private": true, "engines": { "node": "24.21.0"