Files
specs/openspec/changes/archive/2026-10-09-evolve-frontend-stack/design.md
T
ksv741 13e3e53d06
Specs security / security (push) Successful in 8s
Specs verify / verify (push) Successful in 11s
Conventional commits / commits (pull_request) Successful in 12s
Specs security / security (pull_request) Successful in 7s
Specs verify / verify (pull_request) Successful in 11s
spec: finalize wealth-ledger SDD migration
2026-10-09 20:41:46 +03:00

441 lines
43 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 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 уже реализованы, независимые версии остаются отложенными.