spec: archive income page simplification with evidence

This commit is contained in:
2026-09-09 13:54:09 +03:00
parent 4c905c1433
commit fbb7193e90
7 changed files with 219 additions and 57 deletions
@@ -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.
@@ -79,18 +79,33 @@
### Requirement: User explicitly applies an analytics period represented in the URL
Страница выбранного Brokerage Account «Пассивный доход» MUST позволять User редактировать черновик `from` и `to`, не изменяя видимую аналитику до явного применения. Явное применение MUST заменить применённый период, загрузить соответствующий результат и представить применённые даты в URL страницы. Сброс MUST немедленно вернуть применённый период к текущему месяцу по сегодняшний день и синхронизировать URL. Перезагрузка или открытие URL с допустимым периодом MUST восстановить тот же применённый период. Действие перехода на страницу MUST быть доступно из выбранного Brokerage Account, а не из глобальной навигации.
Страница выбранного 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** видимые итоги, сравнения, динамика и breakdown остаются основанными на последнем применённом периоде
- **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`
@@ -101,44 +116,6 @@
- **WHEN** User сбрасывает пользовательский период
- **THEN** страница немедленно применяет период от первого дня текущего месяца по Europe/Moscow до сегодняшнего дня и синхронизирует URL
### Requirement: Analytics summary and trend remain readable per currency
Страница MUST обозначать фактический результат как «Получено купонами и дивидендами» и для каждой исходной валюты показывать total, coupon subtotal и dividend subtotal выбранного периода. Она MUST объяснять, что tax operations не включены, не называя фактический результат gross, net, суммой до налогов, суммой после налогов или налоговым расчётом. Страница MUST показывать stacked column visualization с отдельными coupon и dividend значениями: период до 45 дней включительно группируется по календарным дням, период от 46 до 180 дней включительно — по календарным неделям, более длинный период — по календарным месяцам. Пустые buckets внутри периода MUST оставаться видимыми с нулевыми значениями. При нескольких валютах явный currency control MUST переключать visualization между ними, а эквивалентные значения MUST оставаться доступными как читаемый текст, не только как графика, цвет или положение.
#### Scenario: Короткий период группируется по дням
- **WHEN** включительная продолжительность выбранного периода не превышает 45 дней
- **THEN** visualization показывает отдельные календарные day buckets с coupon и dividend значениями и сохраняет пустые дни как нулевые buckets
#### Scenario: Средний период группируется по неделям
- **WHEN** включительная продолжительность выбранного периода составляет от 46 до 180 дней
- **THEN** visualization показывает calendar week buckets с раздельными coupon и dividend значениями
#### Scenario: Длинный период группируется по месяцам
- **WHEN** включительная продолжительность выбранного периода превышает 180 дней
- **THEN** visualization показывает calendar month buckets с раздельными coupon и dividend значениями
#### Scenario: User переключает валюту visualization
- **WHEN** результат содержит подходящие выплаты в нескольких валютах и User выбирает одну из них
- **THEN** visualization и его текстовый эквивалент показывают значения только выбранной исходной валюты без изменения итогов остальных валют
### Requirement: Instrument breakdown is traceable to exact Ledger Entries
Для каждой исходной валюты страница MUST показывать instrument breakdown, отсортированный по combined total по убыванию. Каждая строка MUST содержать доступную identity Instrument, coupon subtotal, dividend subtotal, combined total и количество включённых выплат. User MUST иметь возможность перейти от строки к точным исполненным Ledger Entries выбранных Instrument, валюты и периода, которые образовали её значения; drill-down MUST сохранять отрицательные корректировки и MUST NOT включать записи вне аналитического итога.
#### Scenario: Breakdown показывает состав дохода по инструментам
- **WHEN** выбранный период содержит подходящие выплаты нескольких Instrument в одной валюте
- **THEN** страница показывает строки по убыванию combined total с identity, двумя subtotals, combined total и receipt count каждого Instrument
#### Scenario: Drill-down воспроизводит строку breakdown
- **WHEN** User открывает drill-down строки Instrument
- **THEN** он видит точные coupon и dividend Ledger Entries этого Instrument, периода и валюты, сумма и количество которых соответствуют строке
### Requirement: Realized analytics never presents an incomplete result as complete
Система MUST формировать выбранный результат, его comparisons, trend buckets, instrument breakdown и drill-down только после успешного получения всех необходимых cursor pages исполненных операций. Ошибка любой необходимой страницы MUST завершать расчёт восстанавливаемой ошибкой и MUST NOT возвращать частичные totals, comparisons, buckets или breakdown как полный результат. Успешный период без подходящих выплат MUST возвращать нулевой результат и отображаться с явным объяснением пустого состояния.
@@ -177,31 +154,69 @@
- **WHEN** Portfolio обзора успешно загружен, но расчёт current-month summary завершается ошибкой
- **THEN** Portfolio и остальные успешно загруженные блоки остаются доступными, а summary показывает собственное состояние восстановления
### Requirement: Forecast is separate from realized income
### Requirement: Income payments remain traceable to exact Ledger Entries
Страница «Пассивный доход» MUST показывать семантически и визуально отдельный forecast block известных coupon и dividend Future Cash Events на следующие 30 дней от сегодняшней даты независимо от выбранного исторического периода. Forecast totals MUST включать только события coupon и dividend с известной projected amount и MUST оставаться разделёнными по исходным валютам; maturity и offer MUST NOT входить в эти totals. Блок MUST показывать три ближайших подходящих события, обозначать результат как не гарантированный прогноз и предоставлять переход к существующей полной странице «Будущие события». Forecast values MUST NOT входить в realized total, comparisons, trend или instrument breakdown. Ошибка forecast MUST оставаться локальной для блока и MUST NOT скрывать успешно загруженную realized analytics.
В разделе «Выплаты» страница 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: Forecast показывает ближайшие известные выплаты
#### Scenario: Каждая выплата показана отдельной карточкой
- **WHEN** на следующие 30 дней известны coupon или dividend Future Cash Events
- **THEN** блок показывает не более трёх ближайших подходящих событий, source-currency totals известных projected amounts, предупреждение о неопределённости и переход к полному списку будущих событий
- **WHEN** выбранный период содержит несколько включённых Ledger Entries одного или разных Instrument
- **THEN** раздел «Выплаты» сразу показывает отдельную карточку каждой записи в исходном порядке без внешних группировочных блоков, раскрытия, итогов инструмента и receipt count на UI
#### Scenario: События без суммы не искажают forecast total
#### Scenario: Список воспроизводит точный состав дохода
- **WHEN** подходящее coupon или dividend Future Cash Event не имеет известной projected amount
- **THEN** событие не добавляет нулевую или вымышленную сумму в forecast total
- **WHEN** User просматривает загруженный раздел «Выплаты»
- **THEN** он видит все и только включённые coupon и dividend Ledger Entries применённого периода с датой, описанием, доступной identity и исходной суммой
#### Scenario: Maturity и offer не входят в forecast income
#### Scenario: Отрицательная корректировка и повторяющиеся IDs сохраняются
- **WHEN** ближайшие Future Cash Events содержат maturity или offer
- **THEN** forecast block не включает их в coupon или dividend totals
- **WHEN** доход содержит отрицательную корректировку либо несколько включённых entries с одинаковым operation ID
- **THEN** каждая запись остаётся отдельной карточкой, а корректировка отображается с исходным знаком
#### Scenario: Пользовательский исторический период не влияет на forecast
#### Scenario: Валюта видна в сумме каждой выплаты
- **WHEN** User применяет любой допустимый исторический период
- **THEN** forecast block по-прежнему охватывает следующие 30 дней от сегодняшней даты
- **WHEN** подходящие выплаты одного Instrument представлены в нескольких исходных валютах
- **THEN** каждая карточка показывает свою Money с исходной валютой, без отдельного заголовка валютной группы или объединённого мультивалютного итога
#### Scenario: Forecast недоступен при успешной realized analytics
### Requirement: Analytics summary remains readable per currency
- **WHEN** realized analytics успешно загружена, а запрос forecast завершается ошибкой
- **THEN** realized totals, comparisons, trend и breakdown остаются доступными, а forecast block показывает собственную ошибку
Страница 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** эта страница остаётся доступной с прежним поведением независимо от отсутствия прогноза на странице дохода