spec: finalize wealth-ledger SDD migration
Specs security / security (push) Successful in 8s
Specs verify / verify (push) Successful in 11s

This commit was merged in pull request #26.
This commit is contained in:
2026-10-09 21:14:22 +03:00
committed by ksv741
parent 90edab6e33
commit 329d66e767
97 changed files with 3444 additions and 244 deletions
@@ -0,0 +1,440 @@
## Context
Локальная архитектура реализована; независимая публикация отложена. Baseline исследования — frontend `3c99deb`, версия `0.3.0`,
2026-09-20. Требования — в
[proposal](proposal.md) и [delta](specs/frontend-design-system/spec.md).
В приложении уже работают React, TypeScript, Vite, Mantine, TanStack Query,
RHF/Zod, JSON-токены, общий адаптер темы, Storybook, Vitest/RTL/MSW,
dependency-cruiser и Knip. Они сохраняются. Другие production-потребители
общего UI или клиентского SDK в исследованном workspace не обнаружены.
На 2026-09-22 workspace-пакеты `@wealthledger/tokens`, `@wealthledger/ui` и
`@wealthledger/icons` используются локально как private packages. Это принятая
часть перехода. Описанные далее независимый release и Nexus остаются
предложением, отложенным до оценки пилота. Фактическое выполнение и ограничения
сохраняются в [tasks](tasks.md).
## Goals / Non-Goals
**Goals:** три небольшие границы поставки общего фундамента; единый каталог
источников; сохранение внешнего вида и предметных инвариантов; потребление
собранного пакета; ограниченные задачи, исполнимые по одной сессии.
**Non-Goals:** другой UI framework, второй transport/store/form stack,
универсальные CRUD/table/form engines, монорепозиторий всего WealthLedger,
mobile adapter, тёмная тема, массовый rewrite CSS и тестирование внутренностей
Mantine. Календарь платежей требует отдельного продуктового запроса.
## Decisions
### 1. Репозиторий и направления зависимостей
Рекомендация: **npm workspaces внутри существующего frontend**. Корневое
приложение сохраняет `src`, Vite entry, собственные providers и `private: true`.
Новые пакеты располагаются в `packages/tokens`, `packages/icons`, `packages/ui`.
Имена `@wealthledger/*` приняты для локальных packages; регистрация scope
и внешняя registry-поставка не подтверждены.
| Вариант | Польза | Цена в этом проекте | Решение |
| --- | --- | --- | --- |
| Три workspace-пакета в frontend | Один lockfile, toolchain, Storybook и атомарный review источников с потребителем | Нужно явно расширить scopes проверок; локальный link может скрыть дефект поставки | Выбран; обязательна проверка packed consumer |
| Один отдельный design-system repo с тремя пакетами | Отдельный owner, release cadence, независимые потребители | Новый CI/catalog/install, синхронизация PR и доступа к registry при одном приложении | Отложить до второго независимого production consumer либо отдельного владельца/release cadence |
| Три отдельных Git repo | Полностью независимые права/жизненные циклы | Три workflow и совместимость без подтверждённой выгоды | Не выбирать |
| Только существующий shared | Уже работает, нет стоимости публикации | Не даёт запрошенной границы версионируемой поставки | Сохраняется до готовности package foundations, не конечная цель |
Направление стрелки — «потребляет»; циклов нет:
```text
frontend app / единственный Storybook
├─ @wealthledger/ui ── @wealthledger/tokens
│ └─ Mantine + React (peers у ui)
├─ @wealthledger/icons ── React (peer)
└─ entities/features/pages → shared/api, domain models, i18n
tokens → нет runtime dependencies
ui → нет icons dependency до появления реального общего consumer
icons → нет ui/tokens/Mantine/router/API dependencies
```
Общие компоненты принимают уже подготовленные строки и slots. `EntryCard`
принимает `ReactNode` и не знает, какой пакет рисует icon/action. Domain выбирает
цвет по смыслу и форматирует деньги; UI только отображает переданное значение.
### 2. Публичная поверхность и сборка
Имена ниже в колонке «Цель» — принятые публичные exports локальных пакетов.
Внутренние `src`, `dist/*` и application alias `@/` не являются package API.
| Пакет | Цель / entrypoints | Состав поставки | Зависимости |
| --- | --- | --- | --- |
| tokens | `@wealthledger/tokens/tokens.json` | Доверенный JSON v1; JS, CSS, React-адаптер и `.d.ts` на старте не нужны | Нет runtime dependencies |
| icons | Именованные `AddIcon`, `EditIcon`, `DeleteIcon` из `@wealthledger/icons` | ESM JS + declarations; CSS только если переносится собственная анимация | React peer + external; React DOM не используется |
| ui | `@wealthledger/ui`: `StateView`, `LoadingSkeleton`, `EntryCard` и их необходимые props types; `@wealthledger/ui/theme`: `theme`, `colorRoles`, `cssVariablesResolver`; `@wealthledger/ui/styles.css` | ESM JS + declarations + собранный CSS, включая CSS Modules | React/React DOM и используемые Mantine core/hooks — peers/external; tokens — точная dependency |
`designTokens` нужен foundations story: адаптер может сохранить существующий
одноимённый export из `ui/theme`, который ссылается на JSON, а не копирует его.
JSON остаётся отдельно доступным без Mantine. Не добавлять JS-обёртку в tokens
только ради импорта JSON. Не выдавать non-web adapter за готовый.
Использовать установленный Vite для library build ui/icons и TypeScript для
declarations. Один `tsc` недостаточен для CSS Modules `EntryCard`. Формат — ESM;
CJS/SSR build не нужен текущему Vite SPA. Настройки library build ещё предстоит
проверить на зафиксированной Vite 8.3.0; готовых конфигураций здесь нет.
Публикуемые `files`/`exports` ограничивают поверхность. CSS явно включается в
side effects; JS импорт сам не устанавливает provider и не читает приложение.
Library build использует отдельную конфигурацию нужных plugins, не наследует
app Vite config целиком: PWA/manifest/service worker и dev API proxy не должны
попасть в поставку библиотеки.
Exports указывают на собранные artifacts, поэтому обычные local/CI entrypoints
сначала собирают packages, затем выполняют consumer typecheck/tests/build.
Сейчас `verify` начинает проверки до app build; этот порядок потребуется
дополнить при 2.2. Acceptance начинается на чистом checkout после `npm ci`,
без старого package dist/cache. Достаточно последовательности npm scripts,
без собственного build orchestrator.
Docker — отдельный потребитель workspace: нынешний `Dockerfile` копирует только
root manifests перед `npm ci`, а `.dockerignore` не допускает packages.
В 2.1–2.2 нужны точечные manifests/source/build-config allowlists, установка
с workspace manifests и package build перед consumer. Host node_modules,
готовый локальный dist и реальные `.env` в контекст не попадают. Development
и production targets проверяются отдельно. В текущем Compose монтируются
только app src/public: изменения packages сначала требуют rebuild dev image.
Отдельный package bind-mount/watch в Compose отложен до фактической потребности;
host development может использовать package watch без изменения root repo.
Сначала peer ranges узкие, по проверенной комбинации React 19.3.0 и Mantine
9.6.1; расширять их только после проверки потребителя. React/React DOM
devDependencies для разработки не должны попадать в bundle. Проверять
`npm ls react react-dom @mantine/core` и содержимое bundle/packed consumer;
`dedupe` не заменяет корректную упаковку. Две несовместимые версии Mantine
также нельзя скрыть локальным symlink.
### 3. Токены: использовать уже существующий JSON
В [tokens.json](../../../../frontend/packages/tokens/tokens.json) уже есть
переносимые значения и описания. **Не менять формат v1 ради упаковки.**
`primitive → semantic → component` — модель владения и ссылок, а не повод
немедленно переименовать все ключи в новый стандарт.
| Уровень | Текущая основа и целевое правило |
| --- | --- |
| Primitive | `color.base`, `color.palettes`, `typography`, `spacing.values`, `radius.values`, `shadows.values`, `breakpoints.values`; исходные эффективные значения сохраняются |
| Semantic | Существующие `color.roles.*` с `color` + `shade` ссылаются на primitive; `primary` с `shade: null` сохраняет разрешение через primaryShade |
| Component | Пока новые не нужны. Добавлять только именованный совместно управляемый контракт реального общего компонента, если semantic role недостаточно; ссылаться на semantic, затем primitive при отсутствии смысловой роли |
Имена описывают роль (`surface`, `border`, `positive`), а не экран или случайный
цвет. Ссылки однонаправленные и разрешимые; обратных ссылок из primitive на
semantic/component нет. Новый color role включает назначение и ограничение
применения. Семантический положительный цвет не вычисляет знак финансовой суммы.
Сохранить единицы: размеры/тени в px адаптируются существующими `rem`/`rgba`,
breakpoints в em не масштабируются, line-height без единицы. Имеющиеся
[theme tests](../../../../frontend/packages/ui/src/theme.test.ts) защищают
именно преобразования и ссылки, не каждое значение JSON.
Общие CSS-роли `--wl-color-*` производит прежний `CSSVariablesResolver`.
Spacing/radius/type scales продолжают поступать в Mantine theme и его CSS
variables. Статическая генерация второго `tokens.css` не нужна: появился бы
ещё один адаптер и риск расхождения scale. JSON экспортируется без сборочного
token engine; Style Dictionary, DTCG-converter и runtime schema отложены до
конкретного внешнего потребителя/формата. Источник остаётся доверенным файлом,
не внешним пользовательским документом.
Светлая схема сохраняется. `primaryShade.dark` не означает готовую dark theme.
Бизнес-коды, суммы/валюты, московские даты, pagination limits, brand gradients
`BankCardFace` и случайная геометрия SVG не превращаются в глобальные токены.
Первое применение semantic roles — точные эквиваленты gray-3/gray-0/green-6/
red-6 в `EntryCard`; перенос и эта правка идут отдельно.
### 4. Mantine, тема и providers
`Stack`, `Group`, `Button`, `Paper`, `ActionIcon`, поля, Modal, Dates/Charts
остаются прямыми импортами Mantine. Изменение defaults решается темой.
Wrapper нужен только собственному контракту: busy region у `LoadingSkeleton`,
empty/error composition у `StateView`, layout/image fallback/actions у `EntryCard`.
App владеет MantineProvider, DatesProvider, QueryClientProvider, AuthProvider,
Router и i18n. Storybook имеет свой Mantine/Dates/i18n runtime с той же темой.
UI не создаёт `WealthLedgerProvider`, не импортирует локализацию продукта и
не включает app providers. `ui/styles.css` содержит существующую визуальную
основу и стили общих компонентов; app/preview подключают его явно после
Mantine CSS. Продуктовые route transitions и bank-card styles остаются в app.
Theme Modal transition и глобальная CSS animation сейчас сосуществуют.
Их визуальный конфликт не доказан. После выделения выбрать один механизм
открытия по browser comparison, предпочтительно уже настроенный Mantine
transition; нельзя удалить второй механизм на основании одного сходства кода.
### 5. Иконки и движение
Начальный набор — реально повторяющиеся plus/pencil/trash из Card/Cards views.
Имена `AddIcon`, `EditIcon`, `DeleteIcon` — предлагаемая поверхность, не найденные
exports. Размер задаёт `size` (default 24), геометрия сохраняет `viewBox`, цвет
получается через `currentColor`. Существующие размеры и strokeWidth 1.5/2
сохраняются у callers; выравнивание рисунка не совмещается с переносом.
Канонический исходник SVG для небольшого набора — **SVG-разметка в именованных
React-компонентах** `packages/icons/src/svg/<Name>.tsx`. Это один редактируемый
источник, без параллельных ручных `.svg` и `.tsx`. Отдельные raw SVG-файлы в
первой поставке не публикуются: non-React consumer пока отсутствует. Если
потребуются исходные `.svg` как самостоятельный продуктовый артефакт, перенести
канон в `src/svg/*.svg` и сравнить штатный SVGR CLI с ручным поддержанием;
не писать собственный генератор. Сейчас ни dependency, ни pipeline для этого
не нужны. JS/types несут SVG-компоненты, это явно отражено в составе поставки.
По умолчанию рисунок декоративный (`aria-hidden`, без фокуса). У standalone
meaningful icon потребитель задаёт доступное имя и image semantics. У
`ActionIcon` имя остаётся на действии; SVG не дублирует его. Общий пакет не
выбирает подпись «редактировать операцию» и не получает DTO.
`BankOperationIcon` и `operation-appearance.ts` остаются в entity: mapping
category/kind → рисунок/цвет/анимация — бизнес-логика. Собственная геометрия и
движение рисунка могут перейти в icons позже при реальном reuse. Hover/focus
trigger по сценарию, route transitions, bank/card state остаются приложению.
Существующий `bind-icon-animation` переиспользуется, если переносится тот же
animation contract; он не копируется в пакет ради трёх статичных иконок.
Все переносимые движения обязаны сохранять reduced-motion поведение.
### 6. Каталог источников компонентов
Порядок поиска: **существующие компоненты приложения → собственная библиотека
→ Mantine → уже разрешённые dependencies → обоснованное новое решение**.
Новый компонент появляется у владельца реальной ответственности, а не потому,
что текущий компонент отсутствует в Storybook. Таблица — компактный маршрут,
не каталог каждого JSX-узла. Номера обозначают этапы в [tasks](tasks.md).
| Назначение | Текущий источник | Целевой источник | Правило применения | Этап |
| --- | --- | --- | --- | --- |
| Layout, buttons, inputs, dialogs | `@mantine/core`, `@mantine/dates` | Те же пакеты | Прямые imports; variant/default через theme, не WL-копия каждого primitive | 1, 2 |
| Цветовые роли, шкалы | `shared/theme/tokens.json` | `tokens/tokens.json` + адаптер `ui/theme` | JSON — значения, адаптер — Mantine, бизнес-смысл — consumer | 2 |
| Тема/общие стили | `shared/theme` | `ui/theme`, `ui/styles.css` | Одна тема App/Storybook; providers у runtimes | 2 |
| Busy skeleton region | `shared/ui/LoadingSkeleton` | `ui` | Label и region общие, skeleton shapes принадлежат странице | 3.1 |
| Empty/error | `shared/ui/StateView` | `ui` | Строки/action props; query/error classification вне UI | 3.1 |
| Строка с изображением и значением | `shared/ui/EntryCard` | `ui` | Готовые strings/slots, fallback и responsive actions; без money parsing | 3.2–3.3 |
| Plus/edit/delete | Inline SVG Card/Cards views | `icons` | Named imports; label и disabled у ActionIcon | 4 |
| Навигация и active route | `shared/ui/AppShell` | `app/ui` | Product shell; не публичный компонент ui-пакета | 1.1 |
| Карта и её операции | `entities/bank-card` | Там же | `BankCardFace`, `BankOperationIcon`, `BankSummaryView`, `BankCardFields`, `BankFeedback` — domain | Без переноса |
| Период банковских данных | `entities/bank-card/BankPeriodPicker`, model/period | Там же | Draft/URL/query semantics сохраняются; не универсальный date service | 5, без extraction |
| Период дохода | `pages/account-income/.../IncomePeriodForm.view` | Та же page; pure helpers в её model | Изолировать story от VM, сохранить validation/submit | 5.3 |
| Позиции/структура портфеля | `pages/account/ui/components/PositionCard.view`, `PortfolioStructure.view` | Там же | Page-local композиция допустима; не переделывать под EntryCard | Без переноса |
| Недавние события | `entities/brokerage-event` + Account page | Там же; общий EntryCard из ui | Первый продуктовый пилот, финансовое представление у entity | 3.2 |
| Общие instrument details | `features/instrument-details` | Там же | Реальный reuse stocks/bonds; typed presentation contract отдельно от общего UI | Без переноса |
Последние непакетные источники выбираются через существующие `index.ts`.
В таблице обозначения `shared/ui/...` описывают файлы до миграции; это не разрешение
на deep import. Текущие владельцы указаны в колонке цели.
### 7. Предметный UI, API и конечные словари
Рекомендация: оставить domain UI в `entities/features/pages`. Второй
производственный consumer, которому нужен тот же стабильный domain contract,
и отдельный release owner — проверяемое условие рассмотрения business package.
Storybook не считается таким потребителем. Повторная карточка может потребовать
переноса page → entity внутри приложения, но не нового npm-пакета.
`shared/api` сохраняет snapshot, generated types, typed transport и auth-aware
boundary. Генерируются **типы**, а не весь handwritten client. Query keys,
normalization, freshness и invalidation остаются в entity/feature owner;
не входят ни в generated artifact, ни в ui. Отдельный `api-client` сейчас
потребовал бы отделить Vite environment, auth/retry lifecycle и query policy без
второго потребителя. Вернуться к вопросу при втором независимом приложении.
Backend остаётся единственным editable HTTP source. Frontend получает
предоставленный snapshot, локально генерирует и сравнивает declarations;
соседний backend checkout не становится входом CI. DTO aliases из generated
`paths` сохраняются. UI form models допустимы, когда отличаются от transport.
Для frontend собственных конечных словарей — принятый `as const` + union:
`enum` несовместим с текущим `erasableSyntaxOnly`. Набор operation kinds можно
свести у bank entity и проверить через generated `BankOperation['kind']`;
не объявлять вторую DTO enum. Wire literals сохраняются. `ProblemDetailsDto.code`
остаётся `string`, unknown code получает fallback; нельзя объявлять закрытый
список ошибок. Общий `apiErrorCode` извлекает code, одинаковое recovery
классифицируется ближайшим допустимым owner без обратных FSD imports.
### 8. Политика библиотек
Версии закреплены в frontend package manifests и lockfile. Решения ниже
не означают новой установки или обновления.
| Область | Сейчас | Решение и причина | Этап |
| --- | --- | --- | --- |
| Build/React/Router | Vite, TS, React Compiler, React Router | Сохранить; расширить имеющийся build для library artifacts | 2 |
| Server/local state | TanStack Query / React state / URL | Сохранить owners; Redux из transitive deps не делает его app store | Все |
| Forms/validation | RHF, resolvers, Zod | Сохранить; vocabulary у entity, controller/view банковской формы отдельно | 5.2, 5.4 |
| UI/charts | Mantine; Recharts/react-is обеспечивают peer graph charts | Сохранить, не удалять peer deps из-за отсутствия direct imports | Все |
| Styles/условные классы | CSS Modules, template/ternary, data-* | Сохранить один этот подход; clsx/classnames не вводить без сложности, которой здесь нет | 3–5 |
| Icons | Inline SVG | Унифицировать повторяющиеся общие рисунки в малом собственном наборе | 4 |
| Motion | motion для routes, CSS/native для рисунков | Сохранить по разным владельцам; проверить дублирование Modal отдельно | 5.1 |
| Dates | Day.js + Mantine Dates + native Date/Intl | Сохранить роль каждого; Moscow/ISO policy остаётся domain/page | 5.3 |
| Money | String + BigInt cents, display formatters | Сохранить точность и unknown≠zero; decimal library не добавлять | Все |
| API/errors | openapi-fetch/react-query/typescript | Сохранить; не добавлять axios, второй generator или schema registry | Все |
| Tests | Vitest/RTL/MSW | Сохранить; `fireEvent` принят, user-event только transitively | 2–5 |
| Catalog | Storybook 10.6.0 | Один runtime, расширить glob и границы на packages | 2–4 |
| Quality | Biome, Knip, dependency-cruiser | Дополнить действующие scopes, не ставить Steiger/ещё один analyzer | 1–2 |
| Browser/visual CI | Принятого E2E runner/visual diff нет | Ручной browser proof пилота; Playwright — при повторяемой browser-only регрессии и владельце CI | Отложено |
| Release | release-it + native npm + Gitea | Использовать текущий подход, адаптировать workspace releases; Changesets/Lerna не нужны для трёх пакетов | 6 |
### 9. Предлагаемое уточнение conventions
Это предложения для будущих задач; действующие инструкции сейчас не меняются.
AGENTS остаётся коротким router, постоянный каталог переносится в подходящий
раздел existing UI convention/README после подтверждённого перехода.
| Владелец правила | Конкретное уточнение | Когда |
| --- | --- | --- |
| `.ai/architecture.md` | Package graph вне FSD; src public APIs; app shell; никакого package → app или same-layer обхода | 1–2 |
| `.ai/conventions/ui.md` | Каталог источников и search order; тема против wrapper; props-only общие компоненты | 2–3 |
| UI + Storybook | JSON/semantic roles, explicit CSS import, светлая схема, иконки/currentColor/reduced-motion | 2–4 |
| `.ai/conventions/data.md`, TypeScript/React | Generated vocabulary vs local const unions; errors открыты; package не владеет query state | 5.2 |
| Engineering/dependencies | Workspace direct deps, peers, exact lock, packed consumer, ограничения exports | 2, 6 |
| Storybook | Один runtime, colocated package/product stories, новые reachability scopes | 2–3 |
| Testing | Поведение → риск → минимальная boundary; package artifact contract; browser proof не равен build | 2–3, 6 |
### 10. Storybook и проверки
Один существующий frontend Storybook показывает `Design System` и `Product`.
Общие stories переезжают вместе с ui/icons; foundations читают опубликованный
JSON/адаптер. Product stories остаются рядом с entities/pages. Обновляются
только globs/aliases/check scopes, не создаётся отдельное приложение каталога.
Mantine/theme/Dates/i18n уже есть в preview; Query/Auth/API providers не нужны.
Synthetic props и callbacks сохраняются; MSW остаётся в Vitest, не в worker.
| Наблюдаемый контракт | Минимальный способ доказать |
| --- | --- |
| JSON → размеры/тени/semantic CSS, unscaled breakpoints | Перенести существующие theme unit tests; не проверять каждый literal |
| JS/types/CSS/JSON доступны вне workspace | Pack + чистый consumer с type/build/render и без source aliases |
| EntryCard image fallback, actions при focus/touch | Один небольшой RTL case на собственный fallback/действие, synthetic story и browser review |
| Деньги/version/idempotency/invalid input | Существующие targeted RTL/MSW tests формы; новые cases только если меняется соответствующая boundary |
| Query/auth/recovery | Имеющиеся MSW tests; no real backend; не тестировать это через stories |
| Keyboard/name/reduced motion/narrow layout | Выбранные собственные interactions в browser; примитивные гарантии Mantine не переписывать |
`build:catalog` не исполняет `play`; существующий `storybook.test.tsx` проверяет
лишь выбранные группы. Не обещать screenshot regression по факту сборки.
Snapshot всех компонентов и полный новый E2E stack не планируются.
Новые workspace-папки не охвачены автоматически: нынешние TS/Knip/deps/story
настройки главным образом ориентированы на `src`. Package type/build/lint,
dependencies и запрет app imports вводятся вместе с соответствующим пакетом.
Нельзя временно исключить packages целиком и назвать gate полным. Same-layer
rule сначала проверяется на текущем graph, затем включается для чистого scope;
legacy exception имеет имя, причину и задачу удаления. Existing coverage floors
не понижаются; перенос tests/coverage ownership не должен скрыть потерю сценария.
### 11. Локальная работа и потребление
Приняты приватные npm workspaces внутри frontend: один install/lockfile,
сборка пакетов и потребление через exports. Проверки охватывают приложение,
пакеты и Storybook. Собранные tarballs проверены вне workspace без source aliases;
эта проверка выявляет отсутствующие exports, типы, CSS и несовместимые peers.
Публикация, независимый SemVer, package release automation и настройка Nexus
не входят в текущий переход. Вернуться к ним можно при подтверждённой пользе
для независимого потребителя; тогда отдельно определить registry, права,
неизменность версий, совместимость и откат. Текущий app release не меняется.
## UI/UX Applicability and Package
**Applicability: N/A для нового визуального дизайна.** Цель — сохранение уже
существующего приложения, без новых экранов и рисунков. Новый макет/visuals и
hash registry не создаются. Для проверки переноса выбран следующий scope.
Пилот: `AccountPage → useAccountPageViewModel → useBrokerageEvents →
BrokerageEventsPreview → BrokerageEventCard → EntryCard`. Это read-only
preview выплат; `PositionCard` не использует EntryCard и не подходит для
переноса без изменения представления. Generic icons в пилоте отсутствуют и
мигрируют отдельно в Card/Cards.
Сохраняются loaded/loading/error/empty, missing amount как unknown, а не ноль,
registry-date/account metadata, валюта, logo fallback, длинные строки,
preview limit и ссылка полного списка. Сверяются desktop и viewport 375px,
доступный focus и отсутствие горизонтального overflow. У generic EntryCard
actions дополнительно проверяются fine/coarse pointer и reduced motion на
synthetic story. Текущий RecentEvents error-priority не меняется молча.
## Delivery Map
| Area | Status | Architectural outcome or N/A reason |
| --- | --- | --- |
| frontend | affected | Workspaces tokens/icons/ui, ограниченная миграция, checks и package release integration |
| backend | N/A | Пакеты не меняют HTTP/auth/storage; замечание об errors вынесено в отложенный scope |
| specs | affected | Этот change и новые требования к поставке/иконкам; main specs обновятся только после принятой реализации |
| docs | affected | После поставки уточняются design overview и описание frontend container/release boundary |
## Solution Assessment
| Решение | Fit / зрелость / ограничения / стоимость | Выбор |
| --- | --- | --- |
| Существующие npm workspaces + Vite/TS | Стандартная упаковка; не требует нового Git repo; CSS/peers и published surface нуждаются в проверке | Да |
| Отдельный design-system repo | Ясная независимость ownership, но удваивает окружение каталога/CI при одном app | Позже по явному consumer/owner trigger |
| Style Dictionary или собственный token compiler | Полезны нескольким форматам/платформам; сейчас JSON + Mantine adapter уже решают задачу | Нет; runtime trust boundary не меняется |
| Ручные React SVG / SVGR pipeline | Несколько рисунков дешевле поддерживать в одном TSX source; SVGR нужен каноническим raw SVG exports | Ручные SVG-компоненты сейчас, без генератора |
| release-it/native npm / Changesets | Первый стек уже принят; второй полезен большому потоку независимых release contributions | Сохранить существующий app release; независимый package release отложен |
Новые внешние runtime dependencies не требуются. Риск supply chain сохраняется
под существующим security gate; peer/version checks и registry ACL нельзя
заменить предположением о готовом Nexus.
## Risks / Trade-offs
- Workspace links скрывают отсутствующие exports/assets и конфликт React:
независимый packed consumer обязателен.
- Package graph расширяет проверяемую область: старый зелёный `deps src` не
подтверждает новые packages. Scopes расширяются при появлении владельца.
- CSS order/global reset влияют на всё приложение. Сначала перенос без
изменения значений, затем отдельно semantic roles и Modal consolidation.
- Общие исходники в frontend — осознанный выбор для одного потребителя;
публикация не означает уже существующий независимый каталог/команду.
- Изменение view/controller рядом с денежной формой рискованнее UI extraction:
это отдельная поздняя задача с сохранением tests version/idempotency.
## Migration Plan
Границы owners → package foundations → общие компоненты и read-only пилот →
иконки и точечное выравнивание → packed consumer → удаление совместимости.
Единственный подробный backlog и зависимости находятся в [tasks](tasks.md).
Во время перехода `shared/theme` и три старых UI entrypoints могут быть тонкими
re-exports package API. Они не содержат второй JSON, CSS или реализации.
Новые consumers используют package paths; добавлять exports/логику в facade
запрещено. Удаление — когда поиск оставшихся runtime/test/story imports даёт
ноль и app/catalog/packed consumer прошли gates. Старые inline icons живут до
своего consumer batch, но новые копии извлечённых рисунков не добавляются.
### Rollback Contract
| Field | Decision |
| --- | --- |
| Status | required: меняются локальные imports, сборка и владельцы компонентов |
| Trigger | Нет exports/CSS/types, peer conflict, несовместимое представление либо failed pilot/recovery |
| Actions | Вернуть согласованный diff приложения, packages, imports и lockfile; при выпуске приложения использовать прежний app image |
| Data safety | API/storage/money wire format не меняются; не откатывать чужие продуктовые изменения и не сбрасывать пользовательские данные |
| Verification | Повторить соответствующий consumer gate и пилот на прежней версии; проверить восстановление согласованного состояния приложения и packages |
### Осознанно отложенное
| Находка/возможность | Причина и условие возврата |
| --- | --- |
| F8: RecentEvents error приоритетнее cached data | Реальное отличие от общего recovery convention. Не менять при extraction; отдельная product recovery задача после согласования expected stale/error UX, с refetch-error regression test |
| F9: ProblemDetails errors schema/example и validation mapping | Отдельная backend contract задача: metadata + HTTP behavior test, затем предоставленный snapshot; не основание вручную исправлять frontend DTO |
| F12: Основная frontend-quality-guardrails spec требует initial 500 kB и baseline lazy chunks, а действующий verify не содержит такого gate | Отдельно согласовать актуальность сохранённого требования и способ enforcement. Не считать Vite warning блокирующим budget gate; package migration сравнивает фактический размер сборки, не заявляя выполнения отсутствующей проверки |
| Единый recovery classifier всех investment errors | Одинаковые codes ещё не доказывают одинаковый UX; объединять только подтверждённый общий recovery без нарушения FSD |
| Business/API packages, dark/mobile, raw SVG pipeline, Playwright/visual service | Нет принятого второго consumer или значимого browser-only regression scope |
## Решения для подтверждения перед зависимыми действиями
1. Публичные имена `@wealthledger/tokens`, `@wealthledger/icons`, `@wealthledger/ui`,
scope ownership и видимость. Рекомендуется приватная поставка WealthLedger;
другое решение меняет доступ и release setup, не текущие данные.
2. Nexus npm endpoint, владелец publish/read credentials, политика неизменности
и хранения прошлых версий. Эти решения нужны только при отдельном запросе внешней публикации.
3. Расхождение F12 между сохранённой spec и текущими quality-командами:
рекомендуемый default — сохранять требование до отдельного решения владельца,
не ослаблять его и не объявлять выполненным. Автоматизация либо корректировка
требования оформляются отдельным ограниченным scope после уточнения причины.
Остальное — аргументированные defaults данного предложения, а не список
неразрешённых вопросов. Перед реализацией нужен отдельный запрос пользователя;
workspaces внутри frontend уже реализованы, независимые версии остаются отложенными.