docs(specs): archive frontend audit remediation
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
## Context
|
||||
|
||||
Proposal утверждён Change Owner в текущей conversation. Change использует
|
||||
`skip_specs: true`: пользовательское поведение уже определено Requirement
|
||||
`brokerage-account-income-analytics — Instrument breakdown is traceable to exact Ledger Entries`.
|
||||
Уточнение transport representation ниже реализует это требование, не меняя
|
||||
правила расчёта дохода или выбранные пользователем возможности.
|
||||
|
||||
IncomeAnalyticsService возвращает отдельные `instruments` и `entries`.
|
||||
Backend группирует выплаты по приоритету UID, FIGI, ticker/type, operation ID
|
||||
с добавлением валюты. Frontend повторяет эту формулу для поиска состава строки.
|
||||
Формулы пока совпадают; отсутствие явной связи допускает их дальнейшее
|
||||
расхождение без изменения самих денежных итогов.
|
||||
|
||||
Frontend уже имеет page VM/view pattern, общую фабрику test QueryClient и
|
||||
централизованный teardown. Остатки локальны: Events выбирает состояние и читает
|
||||
данные в container; auth tests используют самостоятельные clients, один из
|
||||
которых создаётся при render wrapper; T-Bank mutation tests повторяют client
|
||||
configuration. Большинство остальных замечаний устранено ранее.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Один владелец связи каждой выплаты с aggregate Instrument/currency row.
|
||||
- Frontend получает состав breakdown без восстановления server identity.
|
||||
- Events следует существующему VM/view pattern; тестовый cache lifetime
|
||||
определяется одним test case и сохраняется при rerender.
|
||||
- Audit status и документация опираются на фактически подтверждённую поставку.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Изменение identity priority, денежных алгоритмов, source currencies,
|
||||
pagination, временных границ или прав доступа.
|
||||
- Изменение runtime auth/query/cache policy, общий cleanup приложения,
|
||||
новый framework тестирования или универсальный provider harness.
|
||||
- Изменение governance, исторических audit claims либо восстановление
|
||||
отсутствующих approvals прежних commits по косвенным признакам.
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Backend передаёт opaque membership key каждой income entry
|
||||
|
||||
Income-specific transport type `IncomeAnalyticsEntryDto` расширяет существующий
|
||||
`BrokerageOperationDto` обязательным строковым `instrumentKey`.
|
||||
`IncomeAnalyticsDto.entries` использует этот subtype. Ledger endpoint и его
|
||||
общий DTO не приобретают income-specific обязанностей.
|
||||
|
||||
`instrumentKey` совпадает с `IncomeInstrumentDto.key` соответствующей строки.
|
||||
После отбора подходящих выплат backend один раз вычисляет ключ каждой entry
|
||||
по существующей формуле. Aggregation по инструментам использует уже
|
||||
вычисленный `entry.instrumentKey`, а не повторный вызов формулы. Остальные
|
||||
агрегаты продолжают работать с теми же выплатами и exact decimal helpers.
|
||||
|
||||
Сохраняются состав и порядок top-level entries, порядок выплат внутри группы,
|
||||
сортировка instruments, отрицательные значения и receipt counts. Не вводятся
|
||||
дедупликация записей, новый идентификатор операции или зависимость от
|
||||
уникальности ID, которой текущая коллекция дополнительно не проверяет.
|
||||
Все entries выбранной аналитики имеют membership, даже если upstream не дал
|
||||
UID, FIGI, ticker или instrument type; прежний fallback остаётся у backend.
|
||||
|
||||
Frontend воспринимает значение как opaque string и не разбирает его формат.
|
||||
Изменение backend identity representation впоследствии не требует второй
|
||||
реализации правила в клиенте.
|
||||
|
||||
Alternatives:
|
||||
|
||||
- Только дополнить тесты одинаковой формулы: фиксирует текущие примеры, но
|
||||
оставляет двух владельцев правила; не устраняет исходную причину drift.
|
||||
- Добавить `entryIds` к instruments: компактно, но требует второй связи через
|
||||
ID и дополнительных предположений при одинаковых ID; прямой ключ entry
|
||||
выражает ровно нужное отношение без такого lookup.
|
||||
- Вложить полные entries в каждую строку: устраняет сопоставление, но при
|
||||
сохранении существующего top-level массива дублирует наиболее объёмную часть
|
||||
ответа; удаление массива осложняет совместимость без необходимости.
|
||||
- Shared frontend/backend package с формулой: добавляет межрепозиторную
|
||||
зависимость и по-прежнему раскрывает клиенту server identity algorithm.
|
||||
|
||||
### 2. Income VM собирает и проверяет только переданную связь
|
||||
|
||||
Существующий response-to-display mapper остаётся у Income VM. Он строит
|
||||
`Map<string, entries[]>` одним проходом, добавляя entries к bucket без
|
||||
копирования ранее накопленного массива. Для каждой instrument row берётся
|
||||
bucket её ключа; trend derivation остаётся там же, UI geometry — во view.
|
||||
Клиентская `incomeEntryKey` удаляется.
|
||||
|
||||
На этой границе проверяется целостность нового отношения: membership key
|
||||
присутствует и является непустой строкой, instrument keys однозначны, каждой
|
||||
entry соответствует существующая строка той же валюты, количество записей
|
||||
каждой строки соответствует `receiptCount`. Для непустой строки отсутствие
|
||||
entries не подменяется успешным пустым drill-down. Денежные суммы на frontend
|
||||
повторно не рассчитываются.
|
||||
|
||||
При нарушении связи mapper возвращает явный неуспешный результат, а VM выбирает
|
||||
существующее локальное error/retry состояние аналитики. Это сохраняет уже
|
||||
принятое поведение для неполного результата: не показывать его как полный и
|
||||
не превращать ожидаемую ошибку данных в route crash. Нет fallback на прежнюю
|
||||
формулу, пропуска неизвестных entries или исправления полученных данных.
|
||||
Полностью пустой ответ с пустыми instruments и entries остаётся допустимым.
|
||||
|
||||
Проверка ограничена новым membership seam; общий runtime DTO validator,
|
||||
новые schema libraries и общий error protocol не вводятся. Backend service
|
||||
tests фиксируют соответствие membership, counts и точных итогов для UID/FIGI/
|
||||
ticker/type/operation fallback, валют и отрицательных корректировок. Frontend
|
||||
behavior tests используют opaque keys, не выводимые из identity fields, и
|
||||
отдельно проверяют recoverable отказ при повреждённой связи.
|
||||
|
||||
### 3. Events получает обычный page VM
|
||||
|
||||
Page VM владеет account route input, существующим authenticated entity hook,
|
||||
retry callback и discriminated state loading/error/empty/loaded. Container
|
||||
вызывает один VM и выбирает уже существующие state views. Данные и callbacks
|
||||
передаются props; новые layout wrappers и state-view files не нужны.
|
||||
|
||||
Извлечение сохраняет текущий порядок выбора состояний, поведение при
|
||||
отсутствующем account ID, UI событий и действия восстановления. Изменение
|
||||
фонового refetch поведения не смешивается с переносом обязанностей.
|
||||
Наблюдаемая граница проверки — страница и существующий entity seam.
|
||||
|
||||
### 4. Остатки test clients используют существующую фабрику
|
||||
|
||||
Auth и T-Bank test harnesses используют `createTestQueryClient`; при необходимости
|
||||
query-only wrapper остаётся `createQueryWrapper`. Client создаётся до render
|
||||
React wrapper, один раз на независимый test case, и доступен для cache assertions.
|
||||
Существующий локальный выбор AuthProvider, Mantine, Router и initial entries
|
||||
сохраняется; глобальный client или render-with-everything helper не появляются.
|
||||
|
||||
Создание client внутри обычной функции `renderPage` или `renderRoutes`, которая
|
||||
вызывается один раз на case, само по себе не является дефектом. Перенос нужен
|
||||
там, где создание выполняется в React render wrapper или повторяет default
|
||||
policy. Shared teardown сохраняет порядок cleanup, auth reset, MSW reset.
|
||||
Общая инфраструктура не заменяется, используются уже установленные зависимости
|
||||
и имеющиеся helpers; обязательный trigger Solution Assessment отсутствует.
|
||||
|
||||
### 5. Audit evidence остаётся в существующих durable owners
|
||||
|
||||
Три исходных audit reports сохраняются byte-for-byte как versioned snapshots
|
||||
в docs с однозначным index. Исходные source files и прежние snapshots не
|
||||
редактируются. Единственный live remediation registry получает нормализованное
|
||||
сопоставление повторяющихся claims, текущий change и evidence: одинаковые
|
||||
номера F из разных отчётов не считаются одинаковыми Audit IDs автоматически.
|
||||
|
||||
Существующие исправления отмечаются как подтверждённое состояние с реальными
|
||||
ссылками, без фиктивных implementation tasks. Незакрытые остатки относятся к
|
||||
текущему change; все существенные claims получают disposition, чтобы состав
|
||||
закрытия можно было восстановить из registry. T-Bank cache boundary ссылается
|
||||
на уже существующую завершённую поставку.
|
||||
|
||||
As-built docs обновляются до фактического результата реализации. Completion
|
||||
Evidence принадлежит существующему двухфазному завершению после archive и
|
||||
delivery commits. Новая запись различает прежние implementation commits и
|
||||
собственные наблюдения текущей проверки, не объявляя прежние commits прошедшими
|
||||
несуществовавший SDD lifecycle. Исторические statements о результатах остаются
|
||||
историческими; `done` требует полного действующего evidence contract.
|
||||
|
||||
## Delivery Map
|
||||
|
||||
| Area | Status | Architectural outcome or N/A reason |
|
||||
| --- | --- | --- |
|
||||
| frontend | `affected` | Generated income membership contract, display mapping без identity formula, Events VM и стабильные test clients. |
|
||||
| backend | `affected` | Income entry subtype с membership key, вычисляемым одним owner и используемым instrument aggregation. |
|
||||
| specs | `affected` | Canonical Proposal/Design/Tasks и archive текущего change; main Requirements сохраняются, Delta Specs пропущены. |
|
||||
| docs | `affected` | As-built income/frontend описание, неизменяемые audit sources, их нормализованные статусы и фактическое terminal evidence. |
|
||||
|
||||
### Contract Gate Decision
|
||||
|
||||
| Gate | Status | Reason |
|
||||
| --- | --- | --- |
|
||||
| runtime-openapi | `required` | Меняются backend income response representation и frontend snapshot/generated consumer; нужна проверка runtime document против committed snapshot. |
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- Membership key повторяется у каждой entry и слегка увеличивает response.
|
||||
Это меньше дублирования целых records и исключает formula ownership в UI.
|
||||
- Новый frontend требует обновлённый backend. Совместимость обеспечивается
|
||||
порядком поставки, без бессрочного fallback на старую формулу.
|
||||
- Повреждённое отношение прекращает показ загруженной аналитики вместо тихой
|
||||
потери строк. Проверки этого случая принадлежат public behavior seam.
|
||||
- Существующие backend caches хранят Ledger pages, а analytics response
|
||||
формируется заново; schema migration и новая cache generation не нужны.
|
||||
- Не все предыдущие audit corrections имеют историческое terminal evidence.
|
||||
Текущий change подтверждает наблюдаемое состояние, а не создаёт историю
|
||||
approval задним числом. Непрошедший affected-area gate блокирует завершение.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Сначала backend начинает возвращать additive `instrumentKey`, сохраняя все
|
||||
прежние поля и порядок. Старый frontend продолжает работать с таким ответом.
|
||||
Backend OpenAPI остаётся source authority; frontend snapshot и generated types
|
||||
обновляются существующим workflow, после чего переключается frontend consumer.
|
||||
|
||||
При локальном переключении клиент перезагружается: прежние in-memory analytics
|
||||
results и незавершённые запросы не переносятся в новый consumer. Новый frontend
|
||||
не поставляется раньше backend. Events/test changes независимы от runtime
|
||||
contract и не требуют данных для миграции.
|
||||
|
||||
Проверки соответствия существующим требованиям и canonical affected-area gates
|
||||
предшествуют независимому read-only review. Затем применяется действующий
|
||||
archive/delivery/evidence порядок; Design не объявляет будущие результаты.
|
||||
|
||||
### Rollback Contract
|
||||
|
||||
| Field | Decision |
|
||||
| --- | --- |
|
||||
| Status | `required` |
|
||||
| Trigger | Обнаружено неверное сопоставление выплат, нарушение совместимости income response или регрессия отображения после переключения. |
|
||||
| Actions | Сначала вернуть предыдущий frontend consumer и соответствующие snapshot/types, перезагрузить клиент. Затем при необходимости вернуть backend representation; дополнительное поле совместимо со старым клиентом и может временно оставаться. Events/test changes откатываются независимо при регрессии их поведения. |
|
||||
| Data safety | Изменяются только производные read-only response и клиентские представления; SQLite schema, credentials, Ledger storage и upstream operations не меняются. In-memory данные перечитываются; записи и денежные данные не удаляются. |
|
||||
| Verification | В возвращённой паре consumer/provider income drill-down соответствует исходным Instrument/currency/period и signed totals; runtime OpenAPI совпадает со snapshot, generated types соответствуют ему, affected-area gates и semantic review не выявляют регрессий. |
|
||||
Reference in New Issue
Block a user