spec: archive income page simplification with evidence
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: wealth-ledger-sdd
|
||||
created: 2026-09-09
|
||||
@@ -0,0 +1,147 @@
|
||||
# Completion Evidence: simplify-income-page
|
||||
|
||||
## Outcome and authority
|
||||
|
||||
Finalization consumed independent **PASS** from Validation task
|
||||
`01a085bd-2f84-7801-a9c1-d35abc346df1`, record
|
||||
`.agents/sdd/changes/simplify-income-page/005-validation-2.md` (workspace-relative).
|
||||
All nine implementation Tasks 1.1–1.4 and 2.1–2.5 are complete.
|
||||
Canonical store: `wealth-ledger-specs`.
|
||||
|
||||
The owner authorized separate Validation/Finalization tasks, delivery commits and
|
||||
archive in task `01a085af-72e7-78d3-b8a1-bfda579191a6`: «все ок, давай завершать».
|
||||
This authorization supersedes the earlier request to leave delivery uncommitted.
|
||||
No push or deployment is part of completion.
|
||||
|
||||
## Actual Delivery Map and existing delivery commits
|
||||
|
||||
| Area | Status and delivered result | Delivery commit |
|
||||
| --- | --- | --- |
|
||||
| frontend | affected: calendar draft/apply/reset form, flat individual payment cards, states and responsive fixes; income-owned trend/forecast consumers removed; regression tests updated | `26f229ba3e0f8335dde297bf6c4575f7014e9f28` |
|
||||
| docs | affected: as-built calendar filters, flat payments, removed trend/forecast UI and unchanged analytics API | `7166ee51d181b04f3892ff6757a56b578ee9df64` |
|
||||
| specs | affected: approved Proposal, Delta Specs, inline Design r2 and completed Tasks | `4c905c143368d799c142dbbce3a97d74108d6d28` |
|
||||
| backend | N/A: existing endpoint, buckets, monetary calculations, events and persisted data unchanged; working tree clean | N/A |
|
||||
|
||||
`runtime-openapi`: N/A because neither API side changes; frontend continues using
|
||||
the existing typed response and shared Future Cash Events remain available.
|
||||
No empty backend commit or contract gate was required.
|
||||
|
||||
Delivery commits existed before this evidence was created. After committing,
|
||||
re-expansion of all reviewed roots and explicit paths still matched the independent
|
||||
PASS exactly, including raw content and modes; commit hooks did not alter inputs.
|
||||
The subsequent evidence/archive/sync commit ID is reported outside this file to
|
||||
avoid a self-reference.
|
||||
|
||||
## Verification commands and observed results
|
||||
|
||||
Commands below ran under the applicable `.nvmrc` via
|
||||
`source ~/.nvm/nvm.sh && nvm use --silent`; specs used frontend runtime before
|
||||
`cd ../specs`. Current Finalization repeated only `npm ls --depth=0` in frontend
|
||||
and docs (both exit 0), native status/specs instructions (exit 0), and content
|
||||
binding checks. Package/lock/runtime inputs match. No install or dependency change
|
||||
occurred. Unchanged gates and independent review were consumed without repetition.
|
||||
|
||||
| cwd | Exact verification command | Observed result |
|
||||
| --- | --- | --- |
|
||||
| frontend | `npm run verify` | exit 0: OpenAPI generated comparison, strict TypeScript, Biome, 26 files / 147 tests and coverage, Knip, dependency boundaries, application and catalog builds passed; existing catalog chunk warning nonblocking |
|
||||
| docs | `npm run verify` | exit 0: Markdown validation and whitespace checks passed; 44 Markdown files checked, 0 issues |
|
||||
| specs | `openspec validate simplify-income-page --type change --strict --store wealth-ledger-specs` | exit 0: change valid |
|
||||
| specs | `openspec validate --specs --strict --store wealth-ledger-specs` | exit 0: 25 passed / 0 failed |
|
||||
| frontend | `npm run test:run -- src/pages/account-income/ui/AccountIncomePage.test.tsx src/pages/account-events/ui/AccountEventsPage.test.tsx` | independent Validation exit 0: 2 files / 35 tests |
|
||||
| frontend | `node /tmp/validation-005-browser.cjs > /tmp/validation-005-browser.log 2>&1` | independent Validation exit 0; all acceptance assertions pass, zero page errors, contexts closed |
|
||||
| frontend | `npm run dev -- --host 127.0.0.1 --port 5175 --strictPort` | Vite ready for browser proof; deliberately stopped with Ctrl-C, exit 130 |
|
||||
| frontend, docs, specs | `git diff --check` | exit 0 in each affected repository |
|
||||
|
||||
Exact full command wrappers, cwd, exit codes and logs are retained at workspace
|
||||
`.agents/sdd/changes/simplify-income-page/evidence-004/commands.json` and
|
||||
`evidence-005/commands.json` under that same change envelope. Reused gate logs:
|
||||
`evidence-004/frontend-verify.log`, `docs.log`, `specs-change.log`, `specs-main.log`.
|
||||
Independent browser harness and observations: `evidence-005/browser.cjs`,
|
||||
`browser.log`, `browser-result.json` and PNG captures. Detailed independent review:
|
||||
`evidence-005/review.md`. These are evidence locators, not a competing planning store.
|
||||
|
||||
## Independent semantic and acceptance review
|
||||
|
||||
PASS; no Critical or Important findings. Review covered approved behavior and
|
||||
inline package `simplify-income-page-ui-r2`: raw invalid dates retain text, focus,
|
||||
URL and request count; calendar/keyboard apply, reset, browser navigation and
|
||||
Moscow date boundary hold. Money signs, repeated IDs, server order, complete
|
||||
membership validation and per-currency summaries remain intact. Cached snapshot,
|
||||
empty/error/retry states and independent Future Cash Events navigation pass.
|
||||
|
||||
At 320/480/640/960 px all three fixture cards and complete descriptions remain
|
||||
visible without horizontal overflow; mobile inputs meet 44 px. Actual Chrome
|
||||
200% zoom measured outer width 960, inner width 480, DPR 2 and viewport height 406;
|
||||
both calendars fit (bottom 401.5 and 327.70). Screenshots were independently
|
||||
inspected. The shared Ledger consumer retains its prior clamp behavior.
|
||||
|
||||
Limits: browser proof used synthetic intercepted responses and installed Chrome,
|
||||
not live brokerage data or every browser/zoom setting. A first harness assertion
|
||||
included hidden inputs; the corrected visible-input selector passed on rerun.
|
||||
These are not unresolved implementation findings.
|
||||
|
||||
## Approval and PASS input binding
|
||||
|
||||
Algorithm: `wealth-ledger-input-v1` from context authority. Each entry binds
|
||||
workspace-relative path, kind, executable mode and SHA-256 payload; files are sorted
|
||||
by UTF-8 path and NUL-framed. Only Tasks approval normalizes completed task prefixes;
|
||||
verification includes raw Tasks. No unresolved Git conflicts were present.
|
||||
|
||||
Exact reviewed inventory and decision sources are preserved in workspace
|
||||
`.agents/sdd/changes/simplify-income-page/005-inputs.json` and
|
||||
`evidence-003/owner-decisions.json`. Inventory file raw SHA-256:
|
||||
`2f207a30a2eb8f7d8924e5b1b8d98ad7d7f3ac62fbe9b380094741af3c131743`.
|
||||
|
||||
| Binding | Fingerprint |
|
||||
| --- | --- |
|
||||
| Proposal approval | `sha256:64879ac90bb35ca44922501abbb91d313cdea2d2a47a44bbd7a105d4ff572689` |
|
||||
| Delta Specs approval | `sha256:d65555952f71f8c9080958adbc954589247ac53041baa3090b5c3da5383379c1` |
|
||||
| Design approval | `sha256:000c2fcc1fce0e80805aa0b56380f3a616556461e49e6e86a5e721d2caa79490` |
|
||||
| Tasks approval | `sha256:c99a5e5e5358f30c873b6231119f29d69c6edcf63897e9626d59a967bfd18d3e` |
|
||||
| Raw planning | `sha256:41a86b7d134a978a19714c0c4d04f3cb51935d21a18db26d188f72794d030915` |
|
||||
| Frontend gate, 209 entries | `sha256:f799913a03e5513838df53fb9f0f9c25548ec79558238a8b027aa35091e73b12` |
|
||||
| Docs gate, 63 entries | `sha256:8a9c3bc06dc3a8ddb4f444ecc8a1b5c400804fb822c0cf5cb9d94259695001cf` |
|
||||
| Specs gate, 347 entries | `sha256:509f2a7112061627fe876039b6bf69c1540d74ba0266e620bcc44eefd559631d` |
|
||||
| Independent PASS, 620 entries | `sha256:3893b8301a80dfae92676a8bf2c3f78b294007bdd51b230ce35fb09d177d0d93` |
|
||||
|
||||
All four canonical artifact identities, native Delta Specs membership and approval
|
||||
fingerprints match the original owner decisions. Explicit amendment source:
|
||||
owner task `01a0855f-9b55-7d30-91ca-85636984ca6f`, turn
|
||||
`01a0857c-1dec-7090-9102-1aeec4ce9c65`, including «Да, отдельная карточка каждой выплаты».
|
||||
|
||||
Reviewed roots are the complete frontend/, docs/, specs/ Git tracked and nonignored
|
||||
untracked candidates from `git ls-files --cached --others --exclude-standard -z`,
|
||||
union previous inventory entries (deletions remain missing). This covers planning,
|
||||
relevant main baseline, dependency/config inputs and actual delivery.
|
||||
Explicit workspace paths: root AGENTS.md; sdd-session, sdd-implementation,
|
||||
sdd-validation SKILL.md files; sdd-session/references/workflow-protocol.md.
|
||||
Each gate includes its local instructions. Explicit ignored frontend inputs are
|
||||
.env, .env.local, .env.development, .env.development.local, .env.production and
|
||||
.env.production.local; values are never copied into evidence.
|
||||
|
||||
Exclusions: .git, node_modules, ignored generated build/coverage/cache outputs,
|
||||
this change's `.agents/sdd/changes/simplify-income-page/` navigation/evidence,
|
||||
and its canonical completion-evidence.md. Expected archive/sync/evidence outputs
|
||||
are verified separately; no unrelated input change is hidden by these exclusions.
|
||||
|
||||
## Semantic sync review and completion scope
|
||||
|
||||
Current native status and specs instructions resolved exactly one delta:
|
||||
`specs/brokerage-account-income-analytics/spec.md` inside this change. The main
|
||||
baseline is `openspec/specs/brokerage-account-income-analytics/spec.md` in the
|
||||
canonical specs repository. All operation identities matched and all surviving
|
||||
scenarios in the modified period requirement are retained.
|
||||
|
||||
Approved semantic diff: remove requirements for summary/trend visualization,
|
||||
forecast display and instrument-grouped breakdown; add readable text summary,
|
||||
income-page forecast exclusion and exact flat payment cards; modify explicit
|
||||
period selection to include calendar controls, keyboard operation, validation and
|
||||
no repeated period caption. Preserve the five other requirements and existing
|
||||
Purpose byte-for-byte. The known old forecast wording in Purpose is explicitly
|
||||
deferred by approved Design; it does not authorize restoring removed UI.
|
||||
|
||||
The package is wholly inline Design revision r2: no attachments or immutable reuse
|
||||
inputs exist. Native archive must preserve Design and all planning bytes and
|
||||
relative structure. No Audit ID applies, so no remediation registry mutation is
|
||||
needed. New archive/main-spec outputs receive native strict checks; completion
|
||||
record reports the observed archive path and separate completion commit.
|
||||
@@ -0,0 +1,268 @@
|
||||
## Context
|
||||
|
||||
Изменение ограничено presentation и page orchestration маршрута
|
||||
`/accounts/:accountId/income`. Текущий `AccountIncomePage` выбирает состояния
|
||||
через page VM; VM владеет периодом из URL, формой и запросами, а section views
|
||||
отображают переданные данные. Верхняя форма использует native date inputs;
|
||||
история событий уже использует установленный `@mantine/dates` DateInput.
|
||||
|
||||
Ответ `income-analytics` уже содержит totals, comparisons, instrument breakdown
|
||||
и все включённые entries. VM соединяет entries с инструментами по непрозрачному
|
||||
`instrumentKey`, проверяет валюту и receipt count. Денежные значения и порядок
|
||||
инструментов приходят с backend. Дополнительная подписка income VM на `/events`
|
||||
обслуживает только удаляемый прогноз. Trend view использует отдельную derived
|
||||
модель из buckets того же ответа.
|
||||
|
||||
Ограничения: FSD `app → pages → entities → shared`, чистые section views,
|
||||
существующие Mantine theme и i18n, без новой инфраструктуры или зависимостей.
|
||||
Даты API и URL остаются календарными `YYYY-MM-DD` в Europe/Moscow;
|
||||
формат отображения поля не меняет их смысл.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:** использовать существующий календарный контрол, сохранить явную
|
||||
границу draft/applied period, отобразить полный состав дохода в отдельных
|
||||
карточках выплат и удалить принадлежащие странице зависимости исчезающих разделов.
|
||||
Сохранить проверки целостности membership и текущие денежные вычисления.
|
||||
|
||||
**Non-Goals:** изменение API, серверных buckets, денежных агрегатов, определения
|
||||
Instrument, общих Ledger rows, обзора счёта, самостоятельной страницы будущих
|
||||
событий, общей темы или навигации. Пагинация карточек, виртуализация,
|
||||
дополнительные фильтры и новый каталог компонентов не вводятся.
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Календарные поля адаптируются на границе page form
|
||||
|
||||
Используется Mantine DateInput с `valueFormat="DD.MM.YYYY"` и существующим
|
||||
русским DatesProvider. Форма остаётся под управлением react-hook-form и Zod в
|
||||
page VM; form-context/controlled-field adapter подключает DateInput к ней.
|
||||
Общий date-range widget не создаётся: income требует обязательного периода,
|
||||
запрета будущих дат и явных ошибок, тогда как Ledger допускает другие фильтры.
|
||||
Импорта между page slices нет.
|
||||
|
||||
VM различает видимый черновик и применённые ISO-даты. Ручной ввод, в том числе
|
||||
пустой или недопустимый текст, должен попасть в проверяемый черновик. Адаптер
|
||||
учитывает raw input, календарный выбор и blur; автоматический откат текста
|
||||
DateInput к последнему допустимому значению на blur отключается (`fixOnBlur=false`).
|
||||
Парсинг допускает только существующую календарную дату формата поля, без
|
||||
нормализации вроде 31 февраля в март. Успешный календарный выбор заменяет
|
||||
черновик поля; валидированный submit переводит обе даты в ISO и обновляет URL.
|
||||
Последнее внутреннее допустимое значение DateInput не заменяет ошибочный ввод.
|
||||
|
||||
Календарь ограничивает начало допустимым окончанием и сегодняшней московской
|
||||
датой, окончание — допустимым началом и сегодняшней датой. Невалидное соседнее
|
||||
поле не передаётся в min/max. Ограничения календаря помогают выбору, но Zod
|
||||
остаётся проверкой обязательности, календарной корректности, будущих дат и
|
||||
порядка. Для отсутствующей, некорректной или будущей даты ошибка принадлежит
|
||||
самому полю; при обратном диапазоне — полю «По».
|
||||
|
||||
Адаптация календарной даты использует компоненты года, месяца и дня, как в
|
||||
Ledger, без UTC-преобразования через `toISOString()` пользовательского Date.
|
||||
«Сегодня» и default period определяются существующим московским правилом VM,
|
||||
а не часовым поясом устройства. URL остаётся источником применённого периода;
|
||||
его текущая нормализация, открытие и browser navigation сохраняются.
|
||||
Сброс очищает ошибки и восстанавливает оба поля даже тогда, когда текущий URL
|
||||
уже содержит default period, а пользователь изменил только черновик.
|
||||
|
||||
Альтернативы: native date inputs не обеспечивают согласованный контрол;
|
||||
единый range picker меняет привычное взаимодействие двух полей; копирование
|
||||
всего Ledger filter state переносит неподходящие income семантики. Выбран
|
||||
существующий DateInput с локальной адаптацией формы.
|
||||
|
||||
### 2. Одна карточка соответствует одной выплате
|
||||
|
||||
`IncomePayments` отображает один семантический список: каждый Ledger Entry
|
||||
передаётся существующему `LedgerEntryRow`. Нет внешней Paper-карточки Instrument,
|
||||
заголовков валютных групп, instrument subtotal/total и receipt count на UI.
|
||||
Как в списке позиций портфеля, карточки идут общей колонкой; импортов между
|
||||
page slices и нового row renderer нет. LedgerEntryRow сохраняет дату, описание,
|
||||
доступную identity, signed Money, характеристику выплаты и fallback изображения.
|
||||
|
||||
VM сначала проверяет opaque instrument keys, их непустоту и уникальность,
|
||||
принадлежность каждой entry к известному instrumentKey, совпадение валюты и
|
||||
receipt count. Валюта инструмента должна присутствовать среди totalsByCurrency.
|
||||
Повреждённый ответ идёт целиком в error/retry, без частично заполненного списка.
|
||||
Клиент не восстанавливает identity формулой и не пересчитывает денежные суммы.
|
||||
|
||||
После проверки VM отдаёт плоский список в исходном порядке `analytics.entries`.
|
||||
В ключ строки входят opaque instrumentKey, operation ID, occurrence внутри
|
||||
этой группы и logo source. Одинаковые IDs не схлопываются, в том числе между
|
||||
инструментами. Валюта видна в каждой Money, а общие summary/comparisons по-прежнему
|
||||
разделены по валютам. Серверный порядок не заменяется клиентской сортировкой.
|
||||
|
||||
### 3. Удаление секций завершает их локальное владение
|
||||
|
||||
Income page прекращает вызывать `useBrokerageEvents` и готовить forecast model.
|
||||
Удаляются принадлежащие только этой странице forecast/trend views, derived
|
||||
trend model и неиспользуемые стили/строки этих секций. Shared event entity,
|
||||
её запрос и самостоятельная страница будущих событий сохраняются.
|
||||
|
||||
Backend продолжает возвращать buckets по прежнему контракту; frontend больше
|
||||
не строит из них presentation для income page. Это позволяет ограничить
|
||||
изменение потребителем без миграции transport types и соседних consumers.
|
||||
Удаление полей API ради размера ответа — отдельное изменение контракта,
|
||||
не необходимое для этого решения.
|
||||
|
||||
### 4. Границы состояний и доказательства остаются у страницы
|
||||
|
||||
Top-level loading/error и loaded composition остаются в существующей
|
||||
container/VM/view структуре. Skeleton отражает новые регионы. Пустой результат
|
||||
использует StateView; ошибка запроса или membership — существующий error/retry.
|
||||
При фоновой загрузке того же периода валидный Client Cached Snapshot сохраняется
|
||||
с неблокирующим progress, а после ошибки — с локальным предупреждением и retry
|
||||
согласно frontend UI conventions. При отсутствии данных нового query используется loading, без старого результата.
|
||||
Отдельной подписи применённого периода под формой нет; draft/applied boundary
|
||||
сохраняется и видимые суммы меняются только после применения.
|
||||
|
||||
Проверка опирается на существующие page tests и MSW и защищает период и состав
|
||||
денежных данных: draft/apply/reset/URL, недопустимый ручной ввод, московскую границу дня,
|
||||
несколько валют, отрицательные и повторяющиеся выплаты, повреждённый membership
|
||||
и retry. Удаляемые chart/forecast expectations заменяются наблюдаемым отсутствием
|
||||
секций и запроса events от income page. Сохраняемые денежные/membership assertions
|
||||
не удаляются вместе с Accordion. Адаптивность и keyboard flow оцениваются по
|
||||
пакету ниже; новая test/browser инфраструктура для этого не требуется.
|
||||
|
||||
## UI/UX Applicability and Package
|
||||
|
||||
**Applicability and scope:** `new/changed` — income page: календарные фильтры,
|
||||
плоский список выплат, последовательность секций и loading representation.
|
||||
Общая оболочка, обзор счёта и самостоятельные «Будущие события» вне scope.
|
||||
|
||||
**Package revision:** `simplify-income-page-ui-r2`.
|
||||
|
||||
Пакет полностью inline; дополнительных attachments, external-only макетов
|
||||
и immutable reuse inputs нет. Revision r2 отражает уточнение владельца:
|
||||
убрать подпись периода, заголовки валют и внешние карточки Instrument,
|
||||
переименовать раздел в «Выплаты», показывать отдельную карточку каждой выплаты.
|
||||
|
||||
**Screens, states and interactions:**
|
||||
|
||||
Loaded: существующая ширина content до 960 px, Stack gap `lg`, текущая светлая
|
||||
Mantine theme. h1 страницы; h2 итогов, сравнений и «Выплаты». Нет h3/h4 групп
|
||||
валют или инструментов. Суммы доступны текстом, не только цветом.
|
||||
|
||||
```text
|
||||
Пассивный доход
|
||||
┌ Период ──────────────────────────────────────────┐
|
||||
│ С По │
|
||||
│ [ДД.ММ.ГГГГ] [ДД.ММ.ГГГГ] │
|
||||
│ [Применить] [Сбросить] │
|
||||
└──────────────────────────────────────────────────┘
|
||||
Получено купонами и дивидендами
|
||||
[Итого в валюте · Купоны · Дивиденды]
|
||||
[Остальные валюты отдельными карточками]
|
||||
Налоговые операции не включены в этот результат.
|
||||
Сравнения
|
||||
[Предыдущий период · значения и разницы]
|
||||
[Годом ранее · значения] или текст недоступности
|
||||
Выплаты
|
||||
[Ledger Entry: описание, дата, identity, signed Money]
|
||||
[Следующая Ledger Entry, отдельная карточка]
|
||||
```
|
||||
|
||||
Форма — одна Paper с border, radius `md`, padding `md`, видимым «Период».
|
||||
Подписанные DateInput «С» и «По» имеют placeholder `ДД.ММ.ГГГГ`, штатный
|
||||
календарь по focus/click, `valueFormat="DD.MM.YYYY"`, strict raw validation и
|
||||
`fixOnBlur=false`. Действия отдельной строкой: «Применить» filled, «Сбросить»
|
||||
default. Под формой нет повторяющей даты подписи. Неприменённый черновик не
|
||||
меняет аналитику; ограничения и московское правило описаны в Decision 1.
|
||||
|
||||
«Выплаты» — один `ul`, одна LedgerEntryRow-карточка на `li`, gap `sm`.
|
||||
Не добавляются внешние instrument cards, currency headings, instrument totals,
|
||||
subtotals/count, chevron, раскрытие или внутренние scroll-панели. Метаданные и
|
||||
logo fallback принадлежат существующему LedgerEntryRow. Все строки сразу видимы;
|
||||
денежные значения не обрезаются, при нехватке ширины переносятся.
|
||||
|
||||
| Состояние | Представление |
|
||||
| --- | --- |
|
||||
| Initial loading / новый период без cached data | Busy skeleton повторяет heading, форму, summary/comparisons и отдельные строки выплат; shapes без фокуса, shell сохраняется. |
|
||||
| Loaded | Итоги, сравнения и плоский список всех выплат; валюты указаны в Money, summary/comparisons не смешивают их. |
|
||||
| Изменён draft | Поля отражают ввод; суммы, сравнения и выплаты сохраняют последний применённый результат. |
|
||||
| Invalid draft | Ошибка под соответствующим полем, фокус на первом ошибочном поле, исходный текст и URL сохраняются, запроса нет. |
|
||||
| Apply | Click или Enter проверяет raw draft; только успех меняет URL/query, повторный submit во время обработки блокируется. |
|
||||
| Reset | Немедленно восстанавливает обе даты текущего московского месяца, очищает ошибки и синхронизирует URL даже при изменённом только draft. |
|
||||
| Empty | Фильтры, «Купоны и дивиденды не найдены» / «За выбранный период подходящих исполненных выплат нет.», налоговое пояснение, сравнения; «Выплаты» без фиктивных карточек. |
|
||||
| Error без результата / повреждённый membership | StateView «Не удалось рассчитать пассивный доход», пояснение, «Повторить» текущий applied period; частичные данные скрыты. |
|
||||
| Фоновое обновление того же периода | Cached snapshot и draft сохраняются; progress не блокирует; при ошибке предупреждение с retry. |
|
||||
| Нет year comparison | «Сравнение с годом ранее недоступно.», без вымышленных нулей. |
|
||||
| Отрицательные суммы / неполная metadata | Исходный знак, доступная identity и существующий logo fallback; запись не исключается. |
|
||||
|
||||
Во всех состояниях отсутствуют trend, buckets UI, forecast и его состояния.
|
||||
Keyboard order: «С» → «По» → «Применить» → «Сбросить»; Escape закрывает календарь.
|
||||
Штатные доступные controls Mantine и focus indicator сохранены. Ошибки связаны
|
||||
с inputs; суммы и неинтерактивные строки не получают tabIndex. Все продуктовые
|
||||
подписи принадлежат русскому i18n.
|
||||
|
||||
**Responsive behavior:**
|
||||
|
||||
| Viewport | Форма | Выплаты |
|
||||
| --- | --- | --- |
|
||||
| От `48em` | Два равных поля, отдельная строка кнопок по содержимому. | Одна колонка LedgerEntryRow; identity и сумма используют существующий layout. |
|
||||
| `36em`–`48em` | Поля рядом, кнопки переносятся при необходимости. | Одна колонка, текст и суммы переносятся без обрезки. |
|
||||
| Менее `36em` | Поля и кнопки вертикально на всю ширину, touch target не менее 44 px. | Существующий mobile row: сумма под identity. |
|
||||
|
||||
На 320 px и при увеличенном масштабе страница не требует горизонтального scroll;
|
||||
popover остаётся в viewport. Высота определяется данными, прокрутка общая,
|
||||
пагинация, виртуализация и ограничения количества строк не вводятся.
|
||||
|
||||
**Reused source and differences:** immutable visual reuse `N/A`.
|
||||
DateInput истории, LedgerEntryRow, StateView, LoadingSkeleton и theme —
|
||||
implementation references. Поля income обязательны и запрещают будущие даты;
|
||||
список выплат плоский. Код страницы портфеля не копируется между slices.
|
||||
|
||||
## Delivery Map
|
||||
|
||||
| Area | Status | Architectural outcome or N/A reason |
|
||||
| --- | --- | --- |
|
||||
| frontend | `affected` | Calendar form adapter, плоский список карточек выплат, composition/states и относящиеся к ним проверки; удаление только income-owned trend/forecast consumer code. |
|
||||
| backend | `N/A` | Текущий income-analytics уже возвращает totals/comparisons, отсортированные instruments и полные entries с instrumentKey. Endpoint, buckets, денежные расчёты, events и хранение не меняются. |
|
||||
| specs | `affected` | Canonical planning и последующая синхронизация согласованных delta requirements в capability brokerage-account-income-analytics по lifecycle. |
|
||||
| docs | `affected` | После реализации актуализируется as-built описание пассивного дохода: календарные фильтры, отдельные карточки выплат, отсутствие подписи периода и trend/forecast UI и использование прежнего analytics response. |
|
||||
|
||||
### Contract Gate Decision
|
||||
|
||||
| Gate | Status | Reason |
|
||||
| --- | --- | --- |
|
||||
| runtime-openapi | `N/A` | API contract не меняется ни на одной стороне; frontend потребляет прежний типизированный ответ и прекращает отдельный вызов events только со страницы дохода. Snapshot/types не требуют регенерации. |
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- Открытый список увеличивает высоту и число одновременно отрисованных строк.
|
||||
Это ожидаемая цена согласованного полного состава выплат. Используется один
|
||||
проход группировки в VM и обычный document flow; лимиты, скрытие строк или
|
||||
виртуализация не подменяют согласованное поведение.
|
||||
- Raw input DateInput, permissive calendar parsing и timezone conversion могут
|
||||
незаметно подменить период. Локальный form adapter и строгая проверка видимого
|
||||
черновика защищают эту границу; одних minDate/maxDate недостаточно.
|
||||
- Замена Accordion может потерять повторяющиеся выплаты или ошибочные membership
|
||||
guards при упрощении VM. Сохраняются исходный порядок, occurrence identity и
|
||||
существующий отказ от повреждённого результата.
|
||||
- Удаление forecast UI должно удалить и его query subscription; shared events
|
||||
остаётся нужен самостоятельной странице и не удаляется вместе с consumer.
|
||||
- В текущем main Purpose осталось упоминание отдельного ближайшего прогноза.
|
||||
Согласованная delta явно удаляет требование его показа на income page.
|
||||
Это известная устаревающая формулировка Purpose, не разрешение вернуть UI:
|
||||
native sync сохраняет existing Purpose и незатронутые requirements. В этом
|
||||
change Purpose не переписывается; его последующая редактура требует отдельного
|
||||
разрешённого изменения. Упоминания buckets в сохраняемых серверных инвариантах
|
||||
совместимы с неизменным API и не требуют их визуализации.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Поставка выполняется без серверной или data migration. Сначала обновляется
|
||||
согласованная frontend composition и её адаптация формы в рамках существующих
|
||||
границ; после удаления consumers очищаются их локальные неиспользуемые части.
|
||||
Сохраняемые сценарии периода и membership проходят проверку вместе с новой
|
||||
компоновкой. Затем docs отражает фактически поставленный поток. Синхронизация
|
||||
main specs и archive остаются последующими canonical completion mechanics.
|
||||
|
||||
Предварительный backend rollout, изменение environment и установка новых
|
||||
зависимостей не нужны. API остаётся совместимым со старым и новым frontend.
|
||||
|
||||
### Rollback Contract
|
||||
|
||||
| Field | Decision |
|
||||
| --- | --- |
|
||||
| Status | `N/A` |
|
||||
| Reason | Обратимое read-only изменение frontend UI; auth/security, public API, persistent data и денежные вычисления не меняются. Возврат прежней frontend версии восстанавливает прежнее отображение на том же backend без преобразования или удаления данных. |
|
||||
@@ -0,0 +1,43 @@
|
||||
## Why
|
||||
|
||||
Страница «Пассивный доход» перегружена ненужными пользователю разделами
|
||||
«Динамика» и «Ближайший прогноз». Выбор периода отличается от истории событий
|
||||
портфеля, а просмотр выплат требует раскрывать аккордеоны и проходить лишние уровни группировки.
|
||||
Нужно упростить страницу согласно запросу владельца продукта.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Переработать верхний блок фильтров: выбирать начальную и конечную даты
|
||||
календарным контролом с тем же взаимодействием и форматом `ДД.ММ.ГГГГ`,
|
||||
что в истории событий портфеля. Сохранить явное применение, сброс периода,
|
||||
проверку допустимости дат и восстановление применённого периода из URL.
|
||||
Не дублировать даты отдельной подписью под фильтрами.
|
||||
- Убрать со страницы блок «Динамика».
|
||||
- Заменить «По инструментам» разделом «Выплаты»: одна сразу видимая карточка
|
||||
на каждую выплату, без внешних блоков инструментов, их итогов и количества
|
||||
выплат и без отдельных заголовков валют.
|
||||
- Убрать со страницы блок «Ближайший прогноз».
|
||||
|
||||
Ожидаемый результат: пользователь выбирает период привычным способом и сразу
|
||||
видит итоги, сравнения и все выплаты, образующие полученный доход.
|
||||
|
||||
Non-goals: изменение расчётов дохода, правил отбора выплат, разделения валют,
|
||||
сравнений периодов, обзора счёта или самостоятельной страницы будущих событий.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
Нет.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `specs/brokerage-account-income-analytics/spec.md`: унифицировать выбор
|
||||
периода, убрать требования показывать динамику и ближайший прогноз на
|
||||
странице дохода, представить каждую выплату отдельной карточкой без группировочных блоков.
|
||||
|
||||
## Impact
|
||||
|
||||
Изменение затрагивает пользователей страницы дохода выбранного брокерского
|
||||
счёта, её интерфейс, нормативное описание поведения и пользовательскую
|
||||
документацию. Источники данных и денежные расчёты сохраняются.
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
## REMOVED Requirements
|
||||
|
||||
### Requirement: Analytics summary and trend remain readable per currency
|
||||
|
||||
**Reason**: По согласованному Proposal блок «Динамика» удаляется вместе со сценариями дневной, недельной и месячной группировки и переключения валюты графика. Сохранение этих сценариев требовало бы показывать удаляемый блок.
|
||||
|
||||
**Migration**: Требования к читаемым итогам, разделению валют и объяснению налоговой границы перенесены в `Analytics summary remains readable per currency`. Удаление визуализации не меняет денежные расчёты.
|
||||
|
||||
### Requirement: Forecast is separate from realized income
|
||||
|
||||
**Reason**: По согласованному Proposal со страницы удаляется весь блок «Ближайший прогноз» вместе со сценариями отображения его сумм, событий и локальной ошибки.
|
||||
|
||||
**Migration**: `Income page excludes upcoming forecasts` сохраняет отделение будущих событий от полученного дохода, независимость аналитики от доступности прогноза и доступность самостоятельной страницы «Будущие события».
|
||||
|
||||
### Requirement: Instrument breakdown is traceable to exact Ledger Entries
|
||||
|
||||
**Reason**: По уточнению владельца удаляются внешние карточки Instrument с subtotals, combined total и receipt count, а также валютные группы. Прежние scenarios группировочного breakdown больше не описывают требуемый UI.
|
||||
|
||||
**Migration**: `Income payments remain traceable to exact Ledger Entries` заменяет grouping UI плоским разделом «Выплаты», сохраняя точный состав entries, signed Money, повторяющиеся IDs и внутренние membership guards.
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: User explicitly applies an analytics period represented in the URL
|
||||
|
||||
Страница выбранного Brokerage Account «Пассивный доход» MUST позволять User редактировать черновик `from` и `to`, не изменяя видимую аналитику до явного применения. Верхний блок фильтров MUST предоставлять два подписанных календарных поля начала и окончания периода с тем же взаимодействием выбора даты, что в истории событий портфеля, и отображением дат в формате `ДД.ММ.ГГГГ`. Поля и действия применения и сброса MUST быть доступны с клавиатуры. Явное применение MUST заменить применённый период, загрузить соответствующий результат и представить применённые даты в URL страницы. Недопустимый черновик MUST показывать ошибку соответствующего поля и MUST NOT заменять последний применённый период. Сброс MUST немедленно вернуть применённый период к текущему месяцу по сегодняшний день и синхронизировать URL. Перезагрузка или открытие URL с допустимым периодом MUST восстановить тот же применённый период. Страница MUST NOT дублировать период отдельной подписью под блоком фильтров. Действие перехода на страницу MUST быть доступно из выбранного Brokerage Account, а не из глобальной навигации.
|
||||
|
||||
#### Scenario: Черновик периода не применён
|
||||
|
||||
- **WHEN** User изменяет черновик `from` или `to`, но не применяет его
|
||||
- **THEN** видимые итоги, сравнения и карточки выплат остаются основанными на последнем применённом периоде
|
||||
|
||||
#### Scenario: Выбор дат в верхнем блоке
|
||||
|
||||
- **WHEN** User выбирает начало и окончание периода через календарные поля
|
||||
- **THEN** выбранные даты отображаются в подписанных полях в формате `ДД.ММ.ГГГГ`, а изменение остаётся черновиком до явного применения
|
||||
|
||||
#### Scenario: Управление фильтрами с клавиатуры
|
||||
|
||||
- **WHEN** User управляет верхним блоком фильтров с клавиатуры
|
||||
- **THEN** он может последовательно перейти к обоим полям дат, изменить период и выполнить применение или сброс без мыши
|
||||
|
||||
#### Scenario: Применение периода
|
||||
|
||||
- **WHEN** User явно применяет допустимый черновик периода
|
||||
- **THEN** страница загружает аналитику этого периода и отражает применённые `from` и `to` в URL
|
||||
|
||||
#### Scenario: Недопустимый черновик периода
|
||||
|
||||
- **WHEN** User пытается применить черновик с отсутствующей, некорректной или будущей датой либо с `from` после `to`
|
||||
- **THEN** страница показывает ошибку соответствующего поля и сохраняет последний применённый период, его URL и видимую аналитику
|
||||
|
||||
#### Scenario: Восстановление применённого периода из URL
|
||||
|
||||
- **WHEN** User открывает URL страницы с допустимыми `from` и `to`
|
||||
- **THEN** страница показывает аналитику за указанный применённый период
|
||||
|
||||
#### Scenario: Сброс периода
|
||||
|
||||
- **WHEN** User сбрасывает пользовательский период
|
||||
- **THEN** страница немедленно применяет период от первого дня текущего месяца по Europe/Moscow до сегодняшнего дня и синхронизирует URL
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Income payments remain traceable to exact Ledger Entries
|
||||
|
||||
В разделе «Выплаты» страница MUST показывать единый список карточек точных исполненных Ledger Entries применённого периода в исходном серверном порядке entries. Каждая выплата MUST иметь собственную сразу видимую карточку с описанием, датой, доступной identity Instrument и signed Money в исходной валюте. Страница MUST NOT добавлять внешние группировочные карточки или заголовки по Instrument либо валюте, subtotals инструмента или количество выплат инструмента. Список MUST сохранять отрицательные корректировки и повторяющиеся operation IDs как отдельные записи и MUST NOT включать записи вне аналитического итога. Отказ от группировочного UI MUST NOT отменять проверки принадлежности entries к instrumentKey, валюты и receipt count ответа или приводить к пересчёту денежных итогов на клиенте.
|
||||
|
||||
#### Scenario: Каждая выплата показана отдельной карточкой
|
||||
|
||||
- **WHEN** выбранный период содержит несколько включённых Ledger Entries одного или разных Instrument
|
||||
- **THEN** раздел «Выплаты» сразу показывает отдельную карточку каждой записи в исходном порядке без внешних группировочных блоков, раскрытия, итогов инструмента и receipt count на UI
|
||||
|
||||
#### Scenario: Список воспроизводит точный состав дохода
|
||||
|
||||
- **WHEN** User просматривает загруженный раздел «Выплаты»
|
||||
- **THEN** он видит все и только включённые coupon и dividend Ledger Entries применённого периода с датой, описанием, доступной identity и исходной суммой
|
||||
|
||||
#### Scenario: Отрицательная корректировка и повторяющиеся IDs сохраняются
|
||||
|
||||
- **WHEN** доход содержит отрицательную корректировку либо несколько включённых entries с одинаковым operation ID
|
||||
- **THEN** каждая запись остаётся отдельной карточкой, а корректировка отображается с исходным знаком
|
||||
|
||||
#### Scenario: Валюта видна в сумме каждой выплаты
|
||||
|
||||
- **WHEN** подходящие выплаты одного Instrument представлены в нескольких исходных валютах
|
||||
- **THEN** каждая карточка показывает свою Money с исходной валютой, без отдельного заголовка валютной группы или объединённого мультивалютного итога
|
||||
|
||||
|
||||
### Requirement: Analytics summary remains readable per currency
|
||||
|
||||
Страница MUST обозначать фактический результат как «Получено купонами и дивидендами» и для каждой исходной валюты показывать total, coupon subtotal и dividend subtotal выбранного периода. Она MUST объяснять, что tax operations не включены, не называя фактический результат gross, net, суммой до налогов, суммой после налогов или налоговым расчётом. Значения MUST оставаться доступными как читаемый текст, не только как графика, цвет или положение. Страница MUST NOT показывать блок «Динамика», его график, переключатель валюты графика или текстовый список временных buckets независимо от длительности выбранного периода. Удаление блока MUST NOT изменять итоги или сравнения периодов.
|
||||
|
||||
#### Scenario: Итоги дохода доступны как текст
|
||||
|
||||
- **WHEN** выбранный период содержит исполненные купонные или дивидендные выплаты
|
||||
- **THEN** страница показывает читаемые total, coupon subtotal и dividend subtotal с обозначением фактически полученного дохода и объяснением исключения tax operations
|
||||
|
||||
#### Scenario: Итоги нескольких валют остаются разделёнными
|
||||
|
||||
- **WHEN** результат содержит подходящие выплаты в нескольких исходных валютах
|
||||
- **THEN** страница показывает отдельные читаемые итоги каждой валюты без конвертации и общего итога разных валют
|
||||
|
||||
#### Scenario: Динамика отсутствует для периода любой длительности
|
||||
|
||||
- **WHEN** User открывает аналитику или применяет допустимый период любой длительности
|
||||
- **THEN** страница показывает итоги и существующие сравнения, но не показывает блок «Динамика», график, его переключатель валюты или текстовый список временных buckets
|
||||
|
||||
### Requirement: Income page excludes upcoming forecasts
|
||||
|
||||
Страница «Пассивный доход» MUST NOT показывать блок «Ближайший прогноз», его суммы, список будущих выплат, состояния загрузки, пустоты или ошибки. Forecast values MUST NOT входить в realized total, comparisons, trend buckets или instrument breakdown. Доступность данных Future Cash Events MUST NOT влиять на отображение успешно загруженной realized analytics. Существующая самостоятельная страница «Будущие события» и её доступность из выбранного Brokerage Account MUST сохраняться.
|
||||
|
||||
#### Scenario: Известные будущие выплаты не появляются на странице дохода
|
||||
|
||||
- **WHEN** для выбранного Brokerage Account известны будущие coupon или dividend события с суммой или без неё либо maturity или offer события
|
||||
- **THEN** страница дохода не показывает блок «Ближайший прогноз» и не включает эти будущие события в фактически полученный доход
|
||||
|
||||
#### Scenario: Изменение исторического периода не возвращает прогноз
|
||||
|
||||
- **WHEN** User применяет допустимый исторический период или сбрасывает фильтры
|
||||
- **THEN** страница показывает соответствующую realized analytics без блока «Ближайший прогноз»
|
||||
|
||||
#### Scenario: Данные будущих событий недоступны
|
||||
|
||||
- **WHEN** realized analytics успешно загружена, а данные Future Cash Events недоступны
|
||||
- **THEN** итоги, сравнения и карточки выплат остаются доступными, а страница дохода не показывает состояние ошибки или загрузки прогноза
|
||||
|
||||
#### Scenario: Самостоятельная страница будущих событий сохраняется
|
||||
|
||||
- **WHEN** User переходит из выбранного Brokerage Account на существующую страницу «Будущие события»
|
||||
- **THEN** эта страница остаётся доступной с прежним поведением независимо от отсутствия прогноза на странице дохода
|
||||
@@ -0,0 +1,32 @@
|
||||
## 1. Frontend behavior and presentation
|
||||
|
||||
- [x] 1.1 [frontend] Перевести форму периода income page на управляемые Mantine DateInput, сохранив отдельные draft и applied значения, строгую проверку видимого ввода, московскую границу сегодняшнего дня, явное применение, немедленный сброс и восстановление из URL.
|
||||
- **Covers:** `brokerage-account-income-analytics` — `User explicitly applies an analytics period represented in the URL`
|
||||
- **Verification:** Page UI tests подтверждают формат `ДД.ММ.ГГГГ`, календарный и клавиатурный ввод, отсутствие запроса и сохранение результата/URL при invalid draft, применение допустимого периода, reset при изменённом draft и восстановление applied period из URL.
|
||||
- [x] 1.2 [frontend] Заменить Accordion breakdown плоским разделом «Выплаты» с отдельной LedgerEntryRow-карточкой каждой entry, без внешних instrument cards, их итогов/count и заголовков валют; сохранить исходный порядок entries, opaque membership guards, signed payments и устойчивые keys повторяющихся entries.
|
||||
- **Covers:** `brokerage-account-income-analytics` — `Instrument breakdown is traceable to exact Ledger Entries`; `brokerage-account-income-analytics` — `Income payments remain traceable to exact Ledger Entries`
|
||||
- **Verification:** Page/component tests подтверждают одну карточку на entry в исходном порядке, отсутствие instrument/currency groups и instrument totals/count; несколько валют, отрицательные корректировки, повторяющиеся IDs и повреждённый membership сохраняют согласованные outcomes без disclosure action.
|
||||
- [x] 1.3 [frontend] Удалить из income page trend и forecast composition, derived models, events query subscription и ставшие неиспользуемыми локальные views/styles/i18n, сохранив текстовые totals, currency separation, comparisons и независимую страницу Future Cash Events.
|
||||
- **Covers:** `brokerage-account-income-analytics` — `Analytics summary and trend remain readable per currency`; `brokerage-account-income-analytics` — `Forecast is separate from realized income`; `brokerage-account-income-analytics` — `Analytics summary remains readable per currency`; `brokerage-account-income-analytics` — `Income page excludes upcoming forecasts`
|
||||
- **Verification:** Page/route tests подтверждают текстовые итоги и сравнения без «Динамики» и «Ближайшего прогноза», отсутствие запроса events со страницы income, устойчивость realized analytics при недоступности events API и прежнюю доступность самостоятельной страницы «Будущие события».
|
||||
- [x] 1.4 [frontend] Привести loaded, loading, empty, error и cached-snapshot presentation к inline UI/UX package revision `simplify-income-page-ui-r2`, включая отсутствие отдельной подписи периода, семантическую иерархию, keyboard focus, field errors и responsive layout фильтров и отдельных карточек выплат.
|
||||
- **Covers:** `brokerage-account-income-analytics` — `User explicitly applies an analytics period represented in the URL`; `brokerage-account-income-analytics` — `Instrument breakdown is traceable to exact Ledger Entries`; `brokerage-account-income-analytics` — `Income payments remain traceable to exact Ledger Entries`; `brokerage-account-income-analytics` — `Analytics summary remains readable per currency`; `brokerage-account-income-analytics` — `Income page excludes upcoming forecasts`
|
||||
- **Verification:** UI tests проверяют доступные labels/errors, порядок действий и state selection; scoped review на ширинах 320 px, менее `36em`, `36em`–`48em` и от `48em` подтверждает отсутствие горизонтального scroll, перенос длинных названий и сумм, 44 px touch targets и соответствие package revision.
|
||||
|
||||
## 2. Affected repository delivery
|
||||
|
||||
- [x] 2.1 [frontend] Выполнить canonical frontend gate после завершения income page delivery.
|
||||
- **Covers:** `N/A` — non-mutating gate проверяет frontend implementation уже покрытых Requirements и UI/UX package, не создавая самостоятельного observable behavior.
|
||||
- **Verification:** Под Node 22 выполнить `npm run verify` в `frontend`.
|
||||
- [x] 2.2 [docs] Обновить as-built описание пассивного дохода: календарные draft/applied фильтры, отдельная карточка каждой выплаты без instrument/currency groups и подписи периода, сохранённые totals/comparisons и отсутствие trend/forecast UI при неизменном income-analytics API.
|
||||
- **Covers:** `brokerage-account-income-analytics` — `Analytics summary and trend remain readable per currency`; `brokerage-account-income-analytics` — `Forecast is separate from realized income`; `brokerage-account-income-analytics` — `User explicitly applies an analytics period represented in the URL`; `brokerage-account-income-analytics` — `Instrument breakdown is traceable to exact Ledger Entries`; `brokerage-account-income-analytics` — `Income payments remain traceable to exact Ledger Entries`; `brokerage-account-income-analytics` — `Analytics summary remains readable per currency`; `brokerage-account-income-analytics` — `Income page excludes upcoming forecasts`
|
||||
- **Verification:** `docs/features/brokerage-account-income-analytics.md` описывает только фактически поставленный flow, сохраняет корректные локальные ссылки и не копирует Proposal, Delta Specs, Design или Tasks.
|
||||
- [x] 2.3 [docs] Выполнить canonical docs gate после as-built update.
|
||||
- **Covers:** `N/A` — non-mutating gate проверяет affected docs delivery и ссылочную целостность, не добавляя product behavior.
|
||||
- **Verification:** Под Node 22 выполнить `npm run verify` в `docs`.
|
||||
- [x] 2.4 [specs] Сверить завершённые frontend и docs изменения и отмеченные Tasks со всеми operation sections canonical delta, не изменяя approved semantics и сохраняя backend и runtime-openapi как `N/A`.
|
||||
- **Covers:** `brokerage-account-income-analytics` — `Analytics summary and trend remain readable per currency`; `brokerage-account-income-analytics` — `Forecast is separate from realized income`; `brokerage-account-income-analytics` — `User explicitly applies an analytics period represented in the URL`; `brokerage-account-income-analytics` — `Instrument breakdown is traceable to exact Ledger Entries`; `brokerage-account-income-analytics` — `Income payments remain traceable to exact Ledger Entries`; `brokerage-account-income-analytics` — `Analytics summary remains readable per currency`; `brokerage-account-income-analytics` — `Income page excludes upcoming forecasts`
|
||||
- **Verification:** Requirement-by-requirement comparison с Proposal, Delta Specs, Design package revision `simplify-income-page-ui-r2` и delivery не оставляет unchecked implementation scope или поведения вне approved artifacts; найденный semantic mismatch маршрутизируется в ответственную planning boundary.
|
||||
- [x] 2.5 [specs] Выполнить canonical native strict validation change и main specs после обновления task progress.
|
||||
- **Covers:** `N/A` — structural validation является обязательным gate affected specs repository и не представляет новое пользовательское поведение.
|
||||
- **Verification:** Под Node 22 выполнить `openspec validate simplify-income-page --type change --strict --store wealth-ledger-specs` и `openspec validate --specs --strict --store wealth-ledger-specs` из `specs`.
|
||||
Reference in New Issue
Block a user