diff --git a/.gitea/workflows/commits.yml b/.gitea/workflows/commits.yml index 0fd4494..758a561 100644 --- a/.gitea/workflows/commits.yml +++ b/.gitea/workflows/commits.yml @@ -2,7 +2,7 @@ name: Conventional commits on: pull_request: - types: [opened, synchronize, reopened] + types: [opened, synchronize, reopened, edited] concurrency: group: docs-commits-${{ gitea.event.pull_request.number }} diff --git a/AGENTS.md b/AGENTS.md index b960170..726af28 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,7 +24,12 @@ ## Ветки и релизы Работа — в issue-linked ветке от `develop`, затем PR в `develop`; в `main` напрямую -не пушить. Release/hotfix и синхронизация веток — по [README](README.md#релизы). +не пушить. 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; версии вручную не повышать. diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e73dd7..d08d108 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## [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 diff --git a/README.md b/README.md index 4be690d..a639f07 100644 --- a/README.md +++ b/README.md @@ -74,35 +74,58 @@ WealthLedger помогает частному инвестору понимат Репозиторий выпускается независимо. `develop` — интеграция, `main` — принятые релизы. Обычная работа: issue-linked ветка от актуального `develop`, PR в `develop`; общие правила — в Engineering Workflow по ссылке из AGENTS. -Обычные PR допускают squash; release и обратная синхронизация сохраняют историю. +PR сливается squash или rebase-merge; merge-коммит не используется. `main` +обновляется только fast-forward из `develop`, поэтому история линейна: + +```text +/- --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`. Убедись, что - `develop` содержит последний релиз. Создай `release/` от проверенного - `develop`; `nvm use --silent`, `npm ci`. Hotfix начинается от `main` в - `release/hotfix-` и проходит тот же процесс. +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 в `main` с заголовком `chore(release): X.Y.Z`. После зелёных - `verify`, `security`, `commits` выполни fast-forward-only merge. Если `main` - изменился, согласуй историю и повтори проверки кандидата. -4. Получи удалённый `main`, сравни его HEAD с проверенным release SHA и версией + открой 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 -m "Release X.Y.Z"`, затем отправь только `git push origin refs/tags/vX.Y.Z`. Не запускай release-it повторно на `main`. -5. Верни release-коммит через PR `main → develop` без squash/rebase; проверь, - что `develop` содержит тег. Опубликованные теги не перемещай. При сбое после - merge создай отсутствующий тег без нового bump; до merge исправь и проверь - существующий кандидат без повторного повышения версии. + +Обратная синхронизация не нужна: release-коммит попал в `develop` на шаге 3. +Опубликованные теги не перемещай. При сбое после merge создай отсутствующий тег +без нового bump; до merge исправь и проверь существующий кандидат без +повторного повышения версии. + +Freeze релиза не поддерживается: пока открыт релизный PR, `develop` не двигают. +Срочный fix — обычная ветка `fix/*` от `develop` с последующим релизом; +пути от `main` нет, он сделал бы `main` не-предком `develop`. Исключение +требует явного решения: `release/hotfix-` от `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*` защищены. +администратором, включена блокировка устаревшей ветки, обязательны `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 не включены. diff --git a/adr/021-quiet-reading-and-rare-confirmations.md b/adr/021-quiet-reading-and-rare-confirmations.md new file mode 100644 index 0000000..9012b7b --- /dev/null +++ b/adr/021-quiet-reading-and-rare-confirmations.md @@ -0,0 +1,66 @@ +# 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`. diff --git a/adr/README.md b/adr/README.md index 2d1e314..433e4a1 100644 --- a/adr/README.md +++ b/adr/README.md @@ -25,3 +25,4 @@ - [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) diff --git a/architecture/containers.md b/architecture/containers.md index d471fc9..ac28a4a 100644 --- a/architecture/containers.md +++ b/architecture/containers.md @@ -140,12 +140,15 @@ membership текущей revision перед каждым финансовым личные данные. Ротация refresh token остаётся атомарной. SQLite хранит полный последний portfolio, операции и их подтверждённые -диапазоны, private прогнозы, общие instrument/market sources и intent/staging. +диапазоны, private прогнозы, сохранённые точки истории оценки, общие +instrument/market sources и intent/staging. Нормализованные финансовые значения сохраняются decimal-строками. Полнота -отделена от свежести и пагинации. Ответ не ждёт внешних RPC; запрос при -необходимости запускает работу в том же backend process через существующий -лимитер. На startup импорт не запускается; восстановление требует следующего -запроса. Подробности — [модель свежести](cross-cutting/data-freshness.md). +отделена от свежести и пагинации. Ответ не ждёт внешних RPC; обновлением +личных данных владеет часовой обход в том же backend process через +существующий лимитер, недостающие общие источники он же ставит в фоновую +очередь. На startup обход начинает следующую попытку по обычному часовому +сроку; после нового подключения первая попытка начинается сразу. +Подробности — [модель свежести](cross-cutting/data-freshness.md). PUT подключения сначала проверяет token у T-Bank, затем одной транзакцией сохраняет encrypted token, новую revision и проверенный membership. @@ -195,9 +198,8 @@ access token и вращаемый refresh token. [Политика query-кэш Авторизация доступа, недопущение token в публичный ответ и атомарность ротации сессии — отдельные свойства; наличие типизированного клиента -не доказывает их. Известное расхождение регистрации отмечено в -[аутентификации](../domains/authentication/overview.md#регистрация-и-вход), -а действующий формат ошибок описан в [HTTP API](http-api.md#error-contract). +не доказывает их. Действующий формат ошибок описан в +[HTTP API](http-api.md#error-contract). Поддерживается локальный Compose и ограниченная single-server topology с SQLite. Origin браузера, адрес API и cookie policy должны быть согласованы. diff --git a/architecture/cross-cutting/ui-resilience.md b/architecture/cross-cutting/ui-resilience.md index 207b5fc..10ba1ef 100644 --- a/architecture/cross-cutting/ui-resilience.md +++ b/architecture/cross-cutting/ui-resilience.md @@ -16,11 +16,17 @@ пока неизвестно, доступен public или protected маршрут. При загрузке отложенного маршрута уже доступный каркас остаётся видимым. -Фоновое обновление и действие пользователя сохраняют видимые данные или -элемент управления с компактной индикацией процесса. В частности, +Фоновое чтение не сообщает о себе: оно сохраняет видимые данные без индикации +процесса и без технического предупреждения. В частности, [клиентский снимок](data-freshness.md#клиентский-и-серверный-снимки) счетов или -портфеля остаётся доступным после ошибки refetch; сообщение и повтор -относятся к обновлению, а не к отсутствию инвестиций. +портфеля остаётся доступным после ошибки refetch, и эта ошибка не означает +отсутствие инвестиций. Действие пользователя сохраняет видимые данные или +элемент управления и сообщает свой результат, включая неуспешный. + +Первичное ожидание ограничено по времени. По его истечении область переходит +в спокойную локальную недоступность вместо бесконечной загрузки и сама +заменяет её содержимым после восстановления. Решение принято в +[ADR 021](../../adr/021-quiet-reading-and-rare-confirmations.md). Серверная полнота не выводится из query lifecycle. Missing показывает skeleton только отсутствующих секций, partial — подтверждённые строки и @@ -28,20 +34,30 @@ skeleton только отсутствующих секций, partial — по готовятся независимо. Только complete empty означает отсутствие операций. Время источника не заменяется временем получения HTTP-ответа. -Ручное обновление сохраняет показанный результат и applied filters; кнопка -недоступна во время уже запрошенной работы. Ошибка transport отличается от +Экраны просмотра не предлагают ручное обновление или повтор чтения: временные +сбои GET восстанавливаются сами ограниченным числом повторов, сохраняя +показанный результат и applied filters. Ошибка transport отличается от ошибки source work. Status observation прекращается после terminal outcome или ухода; само истечение TTL не запускает импорт. Известный access loss очищает personal view, включая защиту от позднего ответа. +Состояние источника при этом видимо: время последнего успешного результата, +устаревание, исход последней часовой попытки и неполнота показаны текстом либо +доступно именованным элементом рядом с данными, к которым относятся. Это +свойство данных, а не отчёт о техническом процессе, и оно не требует действия. + ## Известная ошибка сценария -Ошибки счетов, портфеля и подключения T-Bank остаются локальными: рядом -с затронутыми данными доступны предусмотренные сценарием retry и navigation -actions. Устаревший серверный Portfolio Snapshot показывает сохранённый -портфель с предупреждением. Он не превращается в глобальный fallback. +Ошибки счетов, портфеля и подключения T-Bank остаются локальными: затронутый +блок показывает собственное состояние и не скрывает успешно отображённые +соседние блоки. Устаревший серверный Portfolio Snapshot показывает сохранённый +портфель с обозначением устаревания. Он не превращается в глобальный fallback. Ошибка источника не означает нулевой баланс или отсутствие операций. +Предусмотренные сценарием navigation actions сохраняются: подтверждённая потеря +доступа предлагает восстановить подключение, а `404` — вернуться к списку +счетов. Это навигация и явные действия пользователя, а не повтор чтения. + ## Непойманная ошибка маршрута Непойманная runtime-ошибка при отображении public или protected route diff --git a/architecture/deployment.md b/architecture/deployment.md index e73dc7b..a8e382f 100644 --- a/architecture/deployment.md +++ b/architecture/deployment.md @@ -12,7 +12,7 @@ Development-команды ниже выполняются из workspace с che - Docker Engine с Compose plugin **не ниже 2.24.4** (`!override` в development), доступ к registry и достаточно свободного диска для non-root UID 1000. - Production images `0.2.3` опубликованы для Linux amd64. + Опубликованные images собраны для Linux amd64 (см. README комплекта). Другую архитектуру проверяют отдельно; multi-platform images не обещаны. - Nexus Docker hosted repository `docker-hosted`, доступный через `nexus.personal.ksv741.ru/docker-hosted/`. Docker Bearer Token Realm включён; @@ -26,7 +26,10 @@ Development-команды ниже выполняются из workspace с che используется сервером для загрузки образов. Для публикации нужны browse/read/add/edit на `docker-hosted`; фактически выданный набор прав настраивается в Nexus. Built-in - workflow token не заменяет эту настройку. + 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. @@ -81,7 +84,8 @@ Workflow отказывается перезаписывать существу Выберите опубликованные 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:0.2.3`. Сохраните versions/revisions +`nexus.personal.ksv741.ru/docker-hosted/wealthledger/frontend:1.1.0` и +`.../backend:2.1.0`. Сохраните versions/revisions в deployment record. Release tags не должны переназначаться другим images. ## Перенос существующих images из Gitea @@ -91,7 +95,9 @@ rollout. В `release.env` комплекта хранится пара полн `REGISTRY_USERNAME`/`REGISTRY_TOKEN` сохраните для отката старого workflow. Пароли вводятся интерактивно или через `--password-stdin`, не через аргументы. -Скопируйте каждый опубликованный release без пересборки. Например, для `0.2.0` +Прежние 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 @@ -114,7 +120,9 @@ artifact повторно не публикуйте; несовпадение т После копирования сравните digests через `skopeo inspect --raw` и SHA-256, выполните `docker pull` обоих images пользователем `wealthledger-ci` на сервере приложения, проверьте OCI revision и container smoke на disposable data. -Только после этого меняйте production image references и запускайте обычную +Серверные шаги (login, pull, smoke) в репозитории не подтверждены и +выполняются оператором. Только после них меняйте +production image references и запускайте обычную последовательность обновления ниже. Старые Gitea packages и references сохраните: откат registry возвращает прежние адреса тех же artifacts, не восстанавливает SQLite и не меняет encryption keys. Не переносите Git release tags и не diff --git a/architecture/http-api.md b/architecture/http-api.md index a1c9bf9..e9e6216 100644 --- a/architecture/http-api.md +++ b/architecture/http-api.md @@ -61,6 +61,17 @@ Envelope несовместим с прежними bare-array ответами: поставляются согласованной парой. Финальный 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 Backend преобразует и проверяет входные данные на общей HTTP-границе. diff --git a/domains/accounts/overview.md b/domains/accounts/overview.md index dc185c0..3c73766 100644 --- a/domains/accounts/overview.md +++ b/domains/accounts/overview.md @@ -62,8 +62,8 @@ - `409 TBANK_NOT_CONNECTED`: список не запрашивается у T-Bank, экран ведёт в настройки. - `400 INVALID_TBANK_TOKEN`: сохранённый токен не удаляется, экран ведёт в настройки для замены или удаления. -- `503 TBANK_UNAVAILABLE`: экран предлагает повторить загрузку или открыть настройки. -- Неизвестная ошибка: экран предлагает только повторить загрузку. +- `503 TBANK_UNAVAILABLE`: экран показывает спокойное состояние недоступности; чтение возобновляется автоматически. +- Неизвестная ошибка: то же состояние недоступности без действия повтора. - Успешный пустой ответ: штатное состояние отсутствия инвестиционных счетов. Карточка ведёт на `/accounts/:accountId` по всей области и доступна с клавиатуры. Карточки расположены в одну колонку при любой ширине. Карточка показывает название, локализованный тип, крупный валютный итог и вторую валюту при наличии, число остальных валют, дату открытия без времени и видимое действие открытия. Отдельные цветные блоки «Акции» и «Облигации» показывают количество наименований бумаг. Недоступная или пустая сводка отображается локально; отсутствие сумм не скрывает известные количества бумаг, а недоступная сводка не подменяет их нулями. Наведение и видимый клавиатурный фокус подчёркивают переход; настройка уменьшения движения отключает анимацию. @@ -71,9 +71,10 @@ ## Свежесть и границы Обычное открытие использует query owner и локальный server snapshot. -«Обновить» запрашивает membership и сводки доступных счетов; активная работа -наблюдается без нового импорта. Истечение TTL само не запускает запрос. -Membership имеет пятиминутную свежесть, portfolio — минутную. Время списка +«Обновить» перечитывает сохранённые membership и сводки доступных счетов; +активная работа наблюдается без нового импорта. Истечение TTL само не +запускает запрос: обновлением личных данных владеет часовой обход. +Membership и portfolio source имеют часовую свежесть. Время списка не подменяет время каждой сводки. Список не заменяет полный [портфель](../portfolio/overview.md), [журнал](../operations/overview.md) или историю закрытых счетов. Для общего результата доступен diff --git a/domains/authentication/overview.md b/domains/authentication/overview.md index 1fa8628..6d1bcf6 100644 --- a/domains/authentication/overview.md +++ b/domains/authentication/overview.md @@ -11,14 +11,20 @@ Пользователь регистрируется или входит по email и паролю. Регистрация сразу создаёт первую сессию. Внешние пробелы email удаляются, буквы приводятся -к нижнему регистру. Backend требует пароль не короче 12 символов и не длиннее -72 UTF-8 байтов. +к нижнему регистру. -**Известное расхождение:** сохранённое требование регистрации и проверка -в форме frontend считают нижнюю границу в UTF-8 байтах, а backend — в символах. -Не-ASCII пароль может пройти форму и получить отказ API. Это не принятое -изменение password policy; требуется отдельное согласованное решение -о проверке пароля и подтверждение полного HTTP-потока. +Пароль при регистрации — от 8 до 72 печатных ASCII-символов: латинские +буквы, цифры, спецсимволы и пробел. Кириллица, emoji, другие не-ASCII и +управляющие символы отклоняются. Пароль не обрезается и не нормализуется, а +обязательных классов символов нет. Верхняя граница вытекает из bcrypt, который +учитывает только первые 72 байта: для ASCII символ равен байту, поэтому +длинный пароль не обрезается молча. Frontend и backend проверяют одну и ту же +политику. + +При входе проверяется только то, что пароль непустой и не длиннее 72 UTF-8 +байтов. Минимум и набор символов не применяются, чтобы пользователь, +зарегистрированный по прежним правилам (в том числе с не-ASCII паролем), мог +войти. Невалидный пароль получает `400` с кодом `VALIDATION_ERROR`. Access token действует 15 минут и хранится только в памяти браузера. Refresh token имеет серверный срок 7 дней, заменяется при восстановлении diff --git a/domains/income/overview.md b/domains/income/overview.md index 25dbe6d..9ec0d21 100644 --- a/domains/income/overview.md +++ b/domains/income/overview.md @@ -4,7 +4,7 @@ купонами и дивидендами за выбранный период». Пользователь открывает `/accounts/:accountId/income` из обзора счёта или [общий доход](../portfolio/all-accounts.md) по доступным счетам. -Экран показывает итоги по валютам, сравнение и отдельную карточку каждой +Экран показывает итоги по валютам и отдельную карточку каждой включённой выплаты. Источником служат [исполненные операции](../operations/overview.md), а не изменение цены портфеля и не календарь будущих выплат. @@ -38,11 +38,12 @@ При первой загрузке или новом периоде без сохранённых данных показан каркас страницы. Пустой результат объясняет отсутствие исполненных купонов и -дивидендов, сохраняя фильтры, налоговое пояснение и сравнения. Ошибка запроса -или нарушение принадлежности выплат даёт локальное состояние с действием «Повторить». +дивидендов, сохраняя фильтры и налоговое пояснение. Ошибка запроса +или нарушение принадлежности выплат даёт локальное состояние недоступности +без действия повтора; чтение восстанавливается автоматически. При фоновом обновлении того же периода сохранённый ответ и черновик -остаются видимыми: загрузку обозначает индикатор, неуспешное обновление — -предупреждение с повторной попыткой. +остаются видимыми без индикации обновления и технического предупреждения. +Неполнота применённого периода обозначается состоянием самого результата. На узком экране поля и действия идут вертикально; все карточки выплат остаются в общем потоке страницы без раскрытия или внутренних областей прокрутки. @@ -51,32 +52,15 @@ - Backend проверяет доступ локально и читает сохранённые операции. Фоновая загрузка должна завершить все страницы; ошибка страницы или повреждённая цепочка курсоров не подтверждают полноту. - Только точные типы купона и дивиденда источника формируют результат. Суммы остаются точными десятичными строками в исходной валюте; отрицательные корректировки сохраняют знак. Налоги, комиссии, сделки, переводы, погашения и переводы дивидендов не входят в доход. -- Итоги, сравнения, временные интервалы и разбиение по инструментам не смешивают валюты и не создают общий конвертированный итог. Страница показывает сравнения с предыдущим периодом и годом ранее, когда календарная дата и история счёта позволяют. Неполное сравнение имеет отдельное сообщение, без нулевой базы. +- Итоги, временные интервалы и разбиение по инструментам не смешивают валюты и не создают общий конвертированный итог. Система не загружает другие периоды ради сравнения полученного дохода с выбранным. - Временные интервалы по-прежнему приходят с backend: дни для 1–45 дней, недели для 46–180 и месяцы для более длинного периода, включая пустые интервалы. Страница дохода не строит из них график или текстовую динамику; ответ API не меняется. -- Раздел «Выплаты» показывает единый список карточек выплат в исходном порядке `entries`: одна карточка на каждую выплату. Внешних групп по инструментам или валютам, итогов и количества выплат инструмента нет. Описание, дата, доступные идентификаторы инструмента и сумма со знаком видны в каждой карточке; валюты по-прежнему разделены в итогах и сравнениях. Дополнительный запрос за составом не нужен. - -Сравнение с непосредственно предыдущим периодом использует предыдущий полный -месяц для полного выбранного месяца, иначе — непосредственно предшествующий -интервал той же включительной длительности. Оно не подменяет сравнение годом -ранее. Незагруженное сравнение и ошибка его источника отличаются от -недоступности годового сравнения по календарю или дате открытия счёта. - -Каждый раздел сравнения показывает цветные панели без общей белой подложки и -внутренних отступов у внешнего блока. Точные периоды показаны в заголовках двух панелей с русским форматом диапазона -и UTC для календарных дат. -Отдельного заголовка с датами и валютного бейджа нет; валюта остаётся у каждой суммы. -Панель «Разница» сохраняет серверную абсолютную разницу и дополняет итог, купоны -и дивиденды процентом: абсолютная разница / сумма годом ранее × 100. -Это округлённый показатель для отображения, а не пересчёт Money или инвестиционная -доходность. Форматирование сохраняет знак и не более одного десятичного разряда. -При нулевой или отрицательной базе либо непредставимом числовом отношении -процентный бейдж не отображается; абсолютная разница остаётся видимой. +- Раздел «Выплаты» показывает единый список карточек выплат в исходном порядке `entries`: одна карточка на каждую выплату. Внешних групп по инструментам или валютам, итогов и количества выплат инструмента нет. Описание, дата, доступные идентификаторы инструмента и сумма со знаком видны в каждой карточке; валюты разделены в итогах. Дополнительный запрос за составом не нужен. ## Полнота и принадлежность выплат Доходы читают сохранённые операции и coverage в согласованной транзакции. -Selected, previous и yearAgo имеют независимую готовность. Неполный selected -не получает окончательных итогов; неполное сравнение не создаёт нулевую базу. +Только выбранный период определяет готовность результата. Неполный период +не получает окончательных итогов. Подтверждённый пустой период отличается от ещё не загруженного. При смене периода прежний snapshot не показывается под новым названием. @@ -103,11 +87,12 @@ Backend обеспечивает принадлежность выплат, ва ## Будущие события и ограничения -На странице дохода нет блоков «Динамика» и «Ближайший прогноз». Она не запрашивает +На странице дохода нет блоков «Динамика» и «Ближайший прогноз», блоков +сравнения периодов и сообщений об их недоступности. Она не запрашивает `/events` и не зависит от его доступности. Самостоятельная страница [«Будущие события»](upcoming-events.md) по-прежнему доступна из обзора выбранного счёта и сохраняет прежнее поведение. Будущие выплаты -не включаются в итоги полученного дохода или сравнения. +не включаются в итоги полученного дохода. Аналитика читает сохранённую историю операций и не рассчитывает налоги, инвестиционную эффективность, доходность, валютную конвертацию или общий мультивалютный итог. diff --git a/domains/income/upcoming-events.md b/domains/income/upcoming-events.md index 4d18cfe..be397aa 100644 --- a/domains/income/upcoming-events.md +++ b/domains/income/upcoming-events.md @@ -39,13 +39,23 @@ ## Период и список -Без фильтра период начинается сегодня в UTC и заканчивается через 30 дней -после начала, обе даты включены. `from` и `to` принимают только строгие даты -`YYYY-MM-DD`; без `from` используется текущая дата UTC, без `to` — `from + 30` -календарных дней. Поля «С» и «По» сохраняют выбранные календарные даты в URL, -а «Сбросить» возвращает период по умолчанию. +Без фильтра страница применяет период по умолчанию из семи календарных +дат включительно: сегодняшней и шести следующих календарных дней. API при +отсутствии параметров использует своё окно (от текущей даты UTC до `from + 30` +календарных дней), обе даты включены; `from` и `to` принимают только строгие +даты `YYYY-MM-DD`. Календарь фильтра разрешает выбор сегодняшней и будущих дат +и не позволяет выбрать даты раньше сегодняшней. Прямая ссылка с валидным +прошедшим периодом сохраняет указанные даты без замены; некорректный формат, +несуществующая календарная дата или обратный диапазон нормализуются к периоду +по умолчанию с удалением параметров из URL. Поля «С» и «По» сохраняют +выбранные календарные даты в URL, а «Сбросить» возвращает период по умолчанию. -События упорядочены по календарной дате. У каждой строки видны тип, +События упорядочены по календарной дате. Купоны и дивиденды показываются +единым предметным представлением выплаты с явной пометкой прогноза — тем же, +что у исполненных выплат в доходе и журнале; неизвестная сумма остаётся +«сумма неизвестна» и не превращается в ноль или полученные деньги. +Дата дивиденда обозначена как дата реестра. Offer и погашение сохраняют своё +значение без суммы выплаты. У каждой строки видны тип, инструмент, дата и доступная сумма в исходной валюте. Список не имеет пагинации и не конвертирует валюты. Пустой результат означает отсутствие известных поддерживаемых событий в выбранном окне, а не доказательство diff --git a/domains/instruments/bonds.md b/domains/instruments/bonds.md index ef47b3e..340a25d 100644 --- a/domains/instruments/bonds.md +++ b/domains/instruments/bonds.md @@ -89,7 +89,11 @@ HTML-источник, общий кэш и поведение при измен Календарь охватывает пять прошлых лет и следующий год. Будущие купоны сортируются по дате выплаты, прошедшие и недатированные доступны отдельно. -Показаны сумма на облигацию, номер, тип, длительность периода и дата реестра, +Будущие и прошедшие события купонного графика показываются единым предметным +представлением выплаты с пометкой события по графику — тем же, что у +исполненных выплат и прогноза; исполнение подтверждает только личная история. +Показаны сумма на облигацию, отдельная личная оценка, номер, тип, длительность +периода и дата реестра, если они известны. Неопределённый будущий купон не заменяется нулём. Личная оценка доступна только при положительных позициях, известной сумме, @@ -117,8 +121,8 @@ HTML-источник, общий кэш и поведение при измен portfolio; они не попадают в общие источники. BondBy переиспользуется зависимостями выпуска. Discovery принадлежит query, -свежесть — конкретному source/range. Missing/stale зависимости запрашиваются -в фоне через прежний limiter; обычный HTTP-ответ не ждёт брокера. +свежесть — конкретному source/range. Missing/stale зависимости ставятся +в фоновую очередь через прежний limiter; обычный HTTP-ответ не ждёт брокера. Покрытые завершённые свечи не перечитываются после restart. Открытый хвост остаётся изменяемым; ручной refresh охватывает показанный период. @@ -127,7 +131,8 @@ BondBy переиспользуется зависимостями выпуск выплат. Наблюдение статуса не запускает повторный импорт. Политика источников и границы — в [модели свежести](../../architecture/cross-cutting/data-freshness.md). -Разделы загружаются независимо, показывают каркас загрузки и локальную повторную попытку. +Разделы загружаются независимо, показывают каркас загрузки и локальное +спокойное состояние недоступности без действия повтора. Ошибка фонового обновления сохраняет последние данные. Смена подключения сбрасывает поддерево клиентских запросов облигаций вместе с остальными финансовыми запросами; выход очищает кэш сессии. Общие график, позиция и личные события @@ -146,6 +151,6 @@ BondBy переиспользуется зависимостями выпуск ## 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. +Страница облигации, поиск и переход по тикеру обращаются к онлайн-справочнику +брокера; сохранённые quotes, свечи, события, ratings и метки портфеля +сохраняют собственные границы свежести и ошибок. diff --git a/domains/instruments/stocks.md b/domains/instruments/stocks.md index e058cf6..fd4cd54 100644 --- a/domains/instruments/stocks.md +++ b/domains/instruments/stocks.md @@ -42,7 +42,8 @@ время получения списка. Изменение условий не заменяет текущий список до следующего нажатия «Найти». Повторный поиск получает новый каталог; страницы одного результата не пересчитываются. Подтверждённый пустой результат отличается -от ошибки источника, для которой интерфейс предлагает повторить поиск. +от ошибки источника: она сообщается рядом с полем поиска, а повторить запрос +пользователь может обычным «Найти» — отдельного действия повтора чтения нет. Ссылка карточки принадлежит выбранному UID; данные разных режимов не смешиваются. Прямые ссылки из портфеля сохраняют @@ -63,8 +64,9 @@ рыночные данные и личная история остаются доступны. Каждый раздел загружается независимо. Первичная загрузка показывает каркас -раздела, ошибка — локальную повторную попытку. При ошибке фонового обновления -сохраняются предыдущие данные и предупреждение. Цена содержит собственное время +раздела, затянувшаяся недоступность — локальное спокойное состояние без +повторной попытки. При ошибке фонового обновления сохраняются предыдущие +данные без технического предупреждения. Цена содержит собственное время UTC, которое может быть старше времени получения ответа. Статус торгов не выводится из свежести цены. Отсутствующее значение не заменяется нулём. @@ -124,7 +126,11 @@ UID включён в ключ кэша и подпись общего курс ## Дивиденды и финансовые показатели -События компании отделены от личных зачислений. Известные будущие выплаты +События компании отделены от личных зачислений. Будущие и прошедшие события +дивидендного графика показываются единым предметным представлением выплаты +с пометкой «событие по графику» — тем же, что у исполненных выплат и прогноза; +сумма на акцию, отдельная личная оценка и дата реестра сохраняют свои подписи +и не выдаются за личное зачисление. Известные будущие выплаты определяются по `paymentDate`, при её отсутствии — по `recordDate`; прошедшие и недатированные события помещены в отдельную историю компании. Отменённые события исключаются. Личная оценка умножает сумму на акцию на текущее положительное @@ -155,14 +161,16 @@ TTM означает последние 12 месяцев, MRQ — послед источники переиспользуются между пользователями, но личные позиции и выплаты всегда проверяют доступ. -Обычное чтение не ждёт RPC; missing/stale зависимости запрашиваются в фоне -через существующий T-Bank limiter. Минутная свежесть применяется к quotes -и открытому хвосту свечей, суточная — к metadata/company/календарям. +Обычное чтение не ждёт RPC; missing/stale зависимости ставятся в фоновую +очередь через существующий T-Bank limiter. Часовая свежесть применяется +к quotes и открытому хвосту свечей; metadata/company/календари, будучи +сохранёнными, не перечитываются обычным чтением. Закрытые полные свечные интервалы, включая пустые, сохраняются после restart. Новый день не делает прежнюю незавершённую свечу полной. -«Обновить» передаёт текущий инструмент, выбранный график и personal-history -scope. Частичные строки видны с предупреждением; неполные личные оценки +Отдельного действия «Обновить» на странице нет: чтение текущего инструмента, +выбранного графика и personal-history scope выполняется автоматически. +Частичные строки видны с обозначением неполноты; неполные личные оценки не становятся нулями. Статус и `sync=observe` не создают новый импорт. Смена подключения и известный access loss очищают личное представление; поздние ответы не возвращают прежние данные. Детали — @@ -193,7 +201,6 @@ scope. Частичные строки видны с предупреждени ## 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. +Страница акции, поиск и переход по тикеру обращаются к онлайн-справочнику +брокера; сохранённые позиции, quotes, свечи и события остаются независимыми +локальными ресурсами и не подменяются ошибкой справочника. diff --git a/domains/operations/overview.md b/domains/operations/overview.md index d3e06fe..b786c21 100644 --- a/domains/operations/overview.md +++ b/domains/operations/overview.md @@ -84,8 +84,10 @@ Coverage/status без изменения rowset cursor не ломают. Пе Полный журнал и превью используют одинаковую компактную строку: логотип или буквенная заглушка, описание, дата, инструмент и денежное движение. -Для купона и дивиденда денежная метка называет точное событие; для прочих -операций — категорию. Она показывается один раз, без повтора рядом с датой. +Исполненные купоны и дивиденды показываются единым предметным представлением +выплаты — тем же, что на странице дохода, в прогнозе и графиках инструментов; +денежная метка называет точное исполненное событие. Для прочих +операций метка называет категорию. Она показывается один раз, без повтора рядом с датой. Знак и цвет обозначают направление денег; отсутствие движения нейтрально. Если описание отсутствует, показывается локализованный тип операции. diff --git a/domains/portfolio/all-accounts.md b/domains/portfolio/all-accounts.md index edc843a..d028588 100644 --- a/domains/portfolio/all-accounts.md +++ b/domains/portfolio/all-accounts.md @@ -53,13 +53,10 @@ Backend публикует статические маршруты под `/api/ - Журнал объединяет операции по убыванию времени, при равном времени использует порядок идентификаторов счетов и операций. ID операций разных счетов не считаются дублями. Переводы не схлопываются. -- Доходы рассчитываются из согласованной локальной выборки операций за общие периоды. - Итоги, сравнения, временные интервалы и принадлежность выплат рассчитывает - сервис дохода. - Для сравнения с годом ранее используется текущий состав счетов; новый счёт - добавляет только реально полученные выплаты. При известных датах открытия всех счетов - доступность сравнения определяется самым ранним открытием в этом составе. - Закрытые счета в историческое сравнение не добавляются. +- Доходы собираются из всех страниц операций каждого счёта за выбранный период. + Итоги, временные интервалы и принадлежность выплат рассчитывает + сервис дохода. Данные сравнения периодов не загружаются и не возвращаются; + состав счетов влияет только на фактические выплаты выбранного периода. - Будущие события сортируются по дате и сохраняют отдельную строку каждого счёта. Прогноз не включается в реализованные доходы. diff --git a/domains/portfolio/data-reference.md b/domains/portfolio/data-reference.md index 4912199..69dd72e 100644 --- a/domains/portfolio/data-reference.md +++ b/domains/portfolio/data-reference.md @@ -32,7 +32,7 @@ | Cash Balance | Суммы из `GetPositions.money`, объединённые по валюте | Заблокированные деньги не прибавляются; торгуемый инструмент `CURRENCY` остаётся позицией | | `totalsByCurrency` | Стоимости позиций + доступные деньги той же валюты | Не включает невычислимую стоимость; не обещает полную оценку портфеля | | `lastSuccessfulAt` | Время формирования успешного снимка, UTC | Не является временем каждой котировки; запасной снимок сохраняет исходное время | -| `isStale` | Истёк минутный срок свежести сохранённого portfolio source | Не описывает состояние клиентского кэша или ошибку фонового запроса | +| `isStale` | Истёк часовой срок свежести сохранённого portfolio source | Не описывает состояние клиентского кэша или ошибку фонового запроса | Ненулевые денежные остатки одной валюты суммируются. Позиции одинаковых инструментов в ответе отдельного счёта не объединяются; визуальная группировка diff --git a/domains/portfolio/how-to/investigate-discrepancy.md b/domains/portfolio/how-to/investigate-discrepancy.md index 20da157..5b5e446 100644 --- a/domains/portfolio/how-to/investigate-discrepancy.md +++ b/domains/portfolio/how-to/investigate-discrepancy.md @@ -12,11 +12,12 @@ величины. Убедитесь, что сравниваемые значения относятся к одному составу счетов и инструменту. Название или тикер сами по себе не доказывают совпадение выпуска; используйте доступные UID, FIGI и ISIN. -2. **Сопоставьте время.** Проверьте `lastSuccessfulAt`, `isStale` и наличие - предупреждения о клиентском обновлении. Серверный снимок не обещает время +2. **Сопоставьте время.** Проверьте `lastSuccessfulAt`, `isStale` и показанный + исход последней часовой попытки. Серверный снимок не обещает время каждой котировки, а общий портфель может соединять разные моменты. - Если запрос завершился ошибкой, доступное действие «Повторить» должно - обновить результат или оставить объяснимую ошибку. Перезагрузка backend + Действия «Повторить» на экране нет: временный сбой чтения восстанавливается + автоматически, поэтому расхождение проверяйте по показанному состоянию + источника, а не по результату ручного обновления. Перезагрузка backend сохраняет последний полный снимок в SQLite, но не создаёт историю стоимости. 3. **Проверьте состав суммы.** Для одной валюты итог складывается из вычислимых стоимостей позиций и доступных денег. Не добавляйте деньги diff --git a/domains/portfolio/overview.md b/domains/portfolio/overview.md index 9f0f791..21afefd 100644 --- a/domains/portfolio/overview.md +++ b/domains/portfolio/overview.md @@ -79,18 +79,23 @@ Backend получает данные через серверную не содержат данных и не получают фокус. При обычном фоновом обновлении сохраняются предыдущие суммы и время успешного получения в UTC. -Серверный `isStale` обозначает истечение минутной свежести снимка. -Клиентское предупреждение означает неудачную попытку обновить уже показанный -ответ. Эти состояния независимы и могут появиться одновременно. -Успешный повторный запрос обновляет суммы и время, снимает клиентскую ошибку; -серверное предупреждение определяется новым `isStale`. +Серверный `isStale` обозначает возраст снимка старше часа: обновлением +владеет часовой обход, а не запрос экрана. Он показывается рядом с суммами +вместе с временем последнего успешного результата и исходом последней часовой +попытки. Неудачная попытка клиента обновить уже показанный ответ о себе не +сообщает: эти состояния независимы, но пользователю видимо только состояние +источника. Успешное автоматическое перечитывание обновляет суммы и время; +обозначение устаревания определяется новым `isStale`. Правила времени и ошибок приведены в [потоке загрузки](flows/portfolio-loading.md). -В текущем решении нет долговременной истории снимков, графиков стоимости -портфеля, валютной конвертации, учёта виртуальных позиций или заблокированных -средств. Последний полный снимок сохраняется после перезапуска; выполнение -синхронизации рассчитано на один экземпляр backend. Действие «Обновить» -перечитывает зависимости открытого экрана независимо от их свежести. +Отдельного чтения истории стоимости нет, но каждый полный снимок +сохраняет точку оценки по счёту и валюте для будущих поддерживаемых +представлений; публичного графика в текущем решении нет. Нет также +валютной конвертации, учёта виртуальных позиций или заблокированных +средств. Последний полный снимок сохраняется после перезапуска; +обновлением личных данных владеет часовой обход, рассчитанный на один +экземпляр backend. Действие «Обновить» выполняет только локальное +перечитывание открытого экрана и не запускает работу с T-Bank. Для проверки конкретной суммы используйте [руководство по расхождениям](how-to/investigate-discrepancy.md). diff --git a/package-lock.json b/package-lock.json index 2a2afcf..9d48345 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "wealth-ledger-docs", - "version": "0.1.4", + "version": "0.1.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "wealth-ledger-docs", - "version": "0.1.4", + "version": "0.1.5", "devDependencies": { "@commitlint/cli": "21.2.2", "@commitlint/config-conventional": "21.2.2", diff --git a/package.json b/package.json index 0002403..ad346c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wealth-ledger-docs", - "version": "0.1.4", + "version": "0.1.5", "private": true, "engines": { "node": "24.21.0"