Files
specs/openspec/changes/archive/2026-11-02-account-top-positions/tasks.md
T
codex-bot 3821d9ed58
Specs security / security (push) Successful in 7s
Specs verify / verify (push) Successful in 14s
refactor: group canonical specs by domain and archive account-top-positions (#74)
2026-11-02 21:50:49 +03:00

24 KiB
Raw Blame History

Tasks

Issue-координатор: WealthLedger/main#48. В каждом affected repo ветка feature/48-account-top-positions создаётся от актуального develop; PR открывается в develop. Тело PR: Summary, Linked Issue WealthLedger/main#48, Changes, Verification, ссылки на связанные PR других repo — по .ai/conventions/engineering-workflow.md («Analysis → Branch», «Validation → PR»). Обозначения PC, PVΔ: PC — portfolio-concentration, PVΔ — delta portfolio-view.

1. Backend: хранение, классификация, DTO, OpenAPI

  • 1.1 [backend] Verification: ветка feature/48-account-top-positions от актуального develop

    • Covers: N/A — организационный шаг cross-repo delivery
    • Source: .ai/conventions/engineering-workflow.md «Analysis → Branch», «Branch → Implementation»
    • Verification: git -C backend branch --show-current выводит feature/48-account-top-positions; git log origin/develop..HEAD пуст до первой задачи
    • Depends on: none
  • 1.2 [backend] Migration: аддитивная модель TbankInstrumentClassification и миграция CREATE TABLE

    • Covers: portfolio-view — Свежесть и fallback полного Portfolio Snapshot
    • Source: design.md «Data / Storage Changes», B1; «Migration / Rollout / Rollback»
    • Verification: npx prisma validate; npx prisma generate; npm run typecheck:app; миграция применена к disposable копии БД с существующими Snapshot, прежние таблицы и строки не изменились; DROP TABLE "TbankInstrumentClassification" на копии возвращает её к исходной схеме
    • Depends on: 1.1
  • 1.3 [backend] Implementation: TbankClient.getEtf, getAssetBrand и opt-in NOT_FOUND для новых вызовов классификации

    • Covers: portfolio-view — Результат получения сектора и эмитента
    • Source: design.md B4 «Новые методы», «NOT_FOUND»; analysis/data-mapping.md «Источники-кандидаты в T-Bank»
    • Verification: npm test -- src/integrations/tbank/tbank.client.spec.ts: NOT_FOUND даёт NotFoundException для bondBy/etfBy/getAssetBy только при opt-in; прежние getBond/getAssetDescription по-прежнему дают Unavailable; PERMISSION_DENIED даёт InvalidToken
    • Depends on: 1.1
  • 1.4 [backend] Implementation: PositionClassificationService — правило 24 ч, четыре исхода по каждому полю, UID→FIGI после NOT_FOUND, дедупликация по инструменту и asset_uid

    • Covers: portfolio-view — Обновление сектора и эмитента; portfolio-view — Результат получения сектора и эмитента; portfolio-view — Нормализованный состав Portfolio
    • Source: design.md B3, B4, B5; analysis/failure-model.md «Классификация отказов по исходам PVΔ «Результат получения сектора и эмитента»», F02–F12
    • Verification: npm test -- src/modules/investments/portfolio: границы 23:59 и 24:00 от walkStartedAt; при отсутствии значения получение идёт в каждом обходе; 4 исхода × {sector, brand}; mismatch не меняет поле; повтор по FIGI после NOT_FOUND по UID; один GetAssetBy на общий asset_uid; используется сохранённый assetUid; InvalidToken пробрасывается, остальные ошибки — нет; типы кроме STOCK/BOND/FUND не запрашиваются
    • Depends on: 1.2, 1.3
  • 1.5 [backend] Implementation: шаг классификации в PortfolioService.queueSnapshot до publish; walkStartedAt из TbankHourlyScheduler; mapper пишет sector/issuer

    • Covers: portfolio-view — Свежесть и fallback полного Portfolio Snapshot; portfolio-view — Нормализованный состав Portfolio
    • Source: design.md B2, B3; analysis/failure-model.md F01, F08
    • Verification: npm test -- src/modules/investments/portfolio/application/portfolio.service.spec.ts: отказ token в классификации → Snapshot не опубликован, вызван rejectMembershipToken; прочая ошибка → Snapshot опубликован, lastSuccessfulAt и isStale не изменены, поля — прежние известные; ошибка GetPortfolio → публикации нет независимо от классификации
    • Depends on: 1.4
  • 1.6 [backend] Implementation: DTO sector и PortfolioIssuerDto (nullable), нормализация старых Snapshot в saved()

    • Covers: portfolio-view — Нормализованный состав Portfolio
    • Source: design.md B6, B7; analysis/system-analysis.md «Семантическая delta PortfolioPositionDto», «Совместимость и миграция»
    • Verification: npm run test:e2e: GET /api/v1/brokerage-accounts/{accountId}/portfolio и /all/portfolio возвращают у каждой позиции sector и issuer; для payload без этих полей — null; после отказа token — 400 INVALID_TBANK_TOKEN
    • Depends on: 1.5
  • 1.7 [backend] Contract Parity: OpenAPI-artifact с новыми полями совпадает с target-delta

    • Covers: portfolio-view — Нормализованный состав Portfolio
    • Source: design.md «Contract Implementation»; analysis/system-analysis.md «Семантическая delta PortfolioPositionDto»
    • Verification: npm run openapi:generate и npm run test:openapi-contract проходят; в корневом openapi.json у PortfolioPositionDto поля sector (string, nullable, required) и issuer (nullable, required, id/name обязательные строки); версия OpenAPI 3.0.x; запросы, маршруты и статусы не изменились (diff схемы с frontend/src/shared/api/spec/openapi.json содержит только эти добавления)
    • Depends on: 1.6
  • 1.8 [backend] Verification: полный gate и PR в develop со ссылкой на WealthLedger/main#48

    • Covers: N/A — финальный gate repo
    • Source: backend .ai/conventions/engineering.md «Проверки и завершение»; .ai/conventions/engineering-workflow.md «Validation → PR»
    • Verification: npm run verify зелёный на финальной ревизии; PR открыт, в теле WealthLedger/main#48 и OpenAPI-artifact приложен как вход для frontend
    • Depends on: 1.7

2. Frontend: контракт, модель, UI

  • 2.1 [frontend] Verification: ветка feature/48-account-top-positions от актуального develop

    • Covers: N/A — организационный шаг cross-repo delivery
    • Source: .ai/conventions/engineering-workflow.md «Analysis → Branch»
    • Verification: git -C frontend branch --show-current выводит feature/48-account-top-positions
    • Depends on: none
  • 2.2 [frontend] Implementation: предоставленный backend snapshot в src/shared/api/spec/openapi.json и generated schema.d.ts

    • Covers: portfolio-view — Нормализованный состав Portfolio
    • Source: design.md B6; frontend .ai/conventions/data.md «Типы и contract»
    • Verification: npm run openapi:generate; npm run openapi:check без diff; npm run typecheck; generated paths содержит sector и issuer у позиции
    • Depends on: 1.7, 2.1
  • 2.3 [frontend] Implementation: pages/account/model/concentration.ts — состав разрезов, учитываемые позиции, валюты, точные суммы и доли, топ-5, «Прочее», порядок равных, имя эмитента, группы «не указан»

    • Covers: portfolio-concentration — Состав разрезов; portfolio-concentration — Названия элементов; portfolio-concentration — Название эмитента при разных названиях бренда; portfolio-concentration — Позиции, учитываемые в разрезе; portfolio-concentration — Сноска о не учтённых позициях; portfolio-concentration — Одна валюта за раз; portfolio-concentration — Доступные валюты вкладки; portfolio-concentration — Топ-5 и строка «Прочее»; portfolio-concentration — Порядок элементов с равной суммой; portfolio-concentration — Расчёт долей; portfolio-concentration — Показ долей; portfolio-concentration — Суммарная доля топ-5; portfolio-concentration — Группы «Сектор не указан» и «Эмитент не указан»
    • Source: design.md F1; analysis/data-mapping.md M3, I1–I8; analysis/test-cases/conc.md
    • Verification: npm run test:run -- src/pages/account/model/concentration.test.ts по oracle TC-CONC-007, TC-CONC-008, TC-CONC-016, TC-CONC-020; сумма точных долей = 100 %; 6,25 → «6,3 %»; «< 0,1 %»; FUND исключён из «Эмитентов»
    • Depends on: 2.2
  • 2.4 [frontend] Implementation: state {accountId, tab, slice, currency} в AccountPage.vm.ts, управляемые Tabs, вкладка только для одного счёта

    • Covers: portfolio-concentration — Вкладка «Концентрация» страницы счёта; portfolio-concentration — Вкладка в состояниях страницы; portfolio-concentration — Переключение разрезов; portfolio-concentration — Начальное состояние вкладки; portfolio-concentration — Независимый выбор валюты вкладки; portfolio-concentration — Состояния Portfolio на вкладке «Концентрация»
    • Source: design.md F2, F3; analysis/test-cases/conc.md TC-CONC-002, TC-CONC-005, TC-CONC-006, TC-CONC-015
    • Verification: npm run test:run -- src/pages/account/ui/AccountPage: вкладки нет для all, при загрузке, ошибке и пустом Portfolio; по умолчанию открыта «Позиции»; разрез и валюта сохраняются при переключении вкладок и сбрасываются при смене счёта; при исчезновении валюты выбирается первая
    • Depends on: 2.3
  • 2.5 [frontend] Implementation: Concentration.view — список, раскрытие «Прочее», пустое состояние, сноска, выбор валюты, Alert «Данные неполные», подавление страничного Alert на вкладке

    • Covers: portfolio-concentration — Раскрытие строки «Прочее»; portfolio-concentration — Строки без переходов; portfolio-concentration — Пустое состояние разреза; portfolio-concentration — Предупреждение о неполных данных
    • Source: design.md F2, F4, «UI Implementation»; analysis/ux-design.md «UI States», «Responsive Behavior»; visuals v2 (SHA-256 по ux-design «Materials» сверены до начала)
    • Verification: npm run test:run -- src/pages/account/ui/AccountPage: при partial на «Концентрации» одно сообщение «Данные неполные», на других вкладках — прежний Alert; строки не являются ссылками; browser-тест 320 px по образцу position-layout.browser.test.tsx без горизонтального скролла страницы
    • Depends on: 2.4
  • 2.6 [frontend] Implementation: DonutChart — центр-оверлей, обводка и штриховка через cellProps, цифры ранга, текстовый эквивалент

    • Covers: portfolio-concentration — Диаграмма распределения разреза; portfolio-concentration — Центр диаграммы; portfolio-concentration — Доступность диаграммы без цвета
    • Source: design.md F5; analysis/ux-design.md «Accessibility», D7; analysis/test-cases/conc.md TC-CONC-027
    • Verification: npm run test:run -- src/pages/account/ui/AccountPage: 7 элементов → 6 сегментов в порядке списка; центр «Топ-5 · 95 %» и «1 000 ₽»; при ≤ 5 элементах только сумма; role="img" и aria-describedby с названиями, суммами и долями
    • Depends on: 2.5
  • 2.7 [frontend] Implementation: подписи секторов через общий словарь stock.sectors + ключ government («Государственный сектор»), видимый и в каталогах

    • Covers: portfolio-concentration — Подписи секторов; portfolio-concentration — Русские подписи известных секторов
    • Source: design.md F6; решение владельца 3084; analysis/test-cases/conc.md TC-CONC-024
    • Verification: npm run test:run: 11 кодов из таблицы PC дают русские подписи; other выводится как есть; тест StockCompany (страница акции): sector: "government" отображается как «Сектор: Государственный сектор» — owner-approved изменение вне Specs (design.md F6, 3084)
    • Depends on: 2.3
  • 2.8 [frontend] Automated Testing: регрессия вкладки «Позиции» и «Структуры портфеля»

    • Covers: portfolio-concentration — Вкладка «Концентрация» страницы счёта
    • Source: analysis/qa-analysis.md «Regression Impact»; analysis/test-cases/reg.md TC-REG-001
    • Verification: существующие AccountPage.test.tsx, position-groups.test.tsx, тесты widgets/portfolio-structure проходят без изменения ожиданий
    • Depends on: 2.6, 2.7
  • 2.9 [frontend] Verification: полный gate и PR в develop со ссылкой на WealthLedger/main#48 и backend PR

    • Covers: N/A — финальный gate repo
    • Source: frontend .ai/conventions/engineering.md; .ai/conventions/engineering-workflow.md «Validation → PR»
    • Verification: npm run verify зелёный на финальной ревизии; PR открыт со ссылками на WealthLedger/main#48 и backend PR
    • Depends on: 2.8

3. Review и QA до G4

  • 3.1 [specs] Independent Review: read-only review backend и frontend PR против Specs, design и analysis (descoped, см. process-record)

    • Covers: portfolio-view — Результат получения сектора и эмитента; portfolio-concentration — Расчёт долей
    • Source: design.md целиком; analysis/system-analysis.md; analysis/failure-model.md
    • Verification: отчёт review приложен к Gate Log G4 в process-record; blocker/major закрыты или возвращены в upstream-артефакт
    • Depends on: 1.8, 2.9
  • 3.2 [specs] QA Verification: прогон test-cases на локальном backend + frontend (descoped, см. process-record)

    • Covers: portfolio-view — Обновление сектора и эмитента; portfolio-view — Свежесть и fallback полного Portfolio Snapshot; portfolio-concentration — Состояния Portfolio на вкладке «Концентрация»
    • Source: analysis/qa-analysis.md «Coverage Matrix», «Entry / Exit Criteria»; analysis/test-cases/conc.md, meta.md, reg.md
    • Verification: результаты TC-CONC-002/005/006/007/008/015/016/020/024/027, TC-META-002/003/004/005, TC-REG-001/004 записаны pass/fail; F01–F16 покрыты; ручной блок (макеты v2, контраст, клавиатура, скринридер) зафиксирован
    • Depends on: 1.8, 2.9
  • 3.3 [specs] QA Verification: exploratory Q-D1/Q-D2/Q-F3 на реальном токене владельца (descoped, см. process-record)

    • Covers: portfolio-view — Обновление сектора и эмитента; portfolio-concentration — Группы «Сектор не указан» и «Эмитент не указан»
    • Source: analysis/qa-analysis.md «Gaps / Blockers», «Test Data and Environment Needs»; решение владельца 3084
    • Verification: записаны полнота сектора и бренда у позиций владельца (доля стоимости без значения, наличие government и фондов без сектора), число RPC и длительность первого обхода, отсутствие RESOURCE_EXHAUSTED / queue full; шаг Portfolio укладывается в WORK_WAIT_BOUND_MS
    • Depends on: 3.2
  • 3.4 [backend] Verification: откат backend-bundle на копии данных до G4 (descoped, см. process-record)

    • Covers: N/A — проверка отката (design.md «Migration / Rollout / Rollback»)
    • Source: design.md «Migration / Rollout / Rollback» (Backend); deploy/README.md «Ручной откат и восстановление»; docs/architecture/deployment.md «Откат image»
    • Verification: на disposable копии БД с применённой миграцией TbankInstrumentClassification: (1) предыдущий backend-image, запущенный как при ручном откате (up --no-deps backend, без сервиса migration), проходит healthcheck, GET Portfolio отвечает 200 на Snapshot с полями sector/issuer; (2) из предыдущего image node node_modules/prisma/build/index.js migrate status и migrate deploy — записаны exit code и вывод (ожидание: status сообщает о миграции, отсутствующей локально; deploy не падает). Результат записан в Gate Log G4; при падении deploy — finding в design
    • Depends on: 1.8

4. Docs (по analysis/docs-impact.md, после merge кода в develop)

  • 4.1 [docs] Verification: ветка feature/48-account-top-positions от актуального develop

    • Covers: N/A — организационный шаг cross-repo delivery
    • Source: .ai/conventions/engineering-workflow.md «Analysis → Branch»; analysis/docs-impact.md «Summary» (правки AS-IS только после merge кода)
    • Verification: backend и frontend PR слиты в develop; git -C docs branch --show-current выводит feature/48-account-top-positions
    • Depends on: 1.8, 2.9, 3.3
  • 4.2 [docs] Docs: домен Portfolio — overview.md, при необходимости concentration.md, data-reference.md, flows/portfolio-loading.md, условная строка в all-accounts.md

    • Covers: portfolio-concentration — Вкладка «Концентрация» страницы счёта; portfolio-view — Нормализованный состав Portfolio
    • Source: analysis/docs-impact.md «Documentation Changes» (строки домена и flow)
    • Verification: npm run verify в docs; текст соответствует реализованному AS-IS
    • Depends on: 4.1
  • 4.3 [docs] Docs: интеграция и архитектура — integrations/tbank.md, architecture/cross-cutting/data-freshness.md, architecture/containers.md

    • Covers: portfolio-view — Свежесть и fallback полного Portfolio Snapshot; portfolio-view — Обновление сектора и эмитента
    • Source: analysis/docs-impact.md «Documentation Changes» (Integration, Architecture); design.md B1, B4
    • Verification: npm run verify в docs
    • Depends on: 4.1
  • 4.4 [docs] Docs: ADR 022 stored-instrument-classification (Accepted, AS-IS), строка в adr/README.md, ссылка в Status ADR 019

    • Covers: portfolio-view — Свежесть и fallback полного Portfolio Snapshot
    • Source: analysis/docs-impact.md «ADR Decision»; design.md «ADR Candidates», «Solution Assessment»; решение владельца 3083
    • Verification: npm run verify в docs; текст ADR 019 кроме строки Status не изменён
    • Depends on: 4.1
  • 4.5 [docs] Docs: глоссарий и навигация — glossary.md, overview.md, README.md

    • Covers: portfolio-concentration — Переключение разрезов
    • Source: analysis/docs-impact.md «Glossary / Navigation Impact»
    • Verification: npm run verify в docs; ссылки из навигации ведут на домен Portfolio
    • Depends on: 4.2
  • 4.6 [docs] Verification: PR в develop со ссылкой на WealthLedger/main#48 и code PR

    • Covers: N/A — финальный gate repo
    • Source: .ai/conventions/engineering-workflow.md «Validation → PR»
    • Verification: npm run verify зелёный; PR открыт со ссылками на WealthLedger/main#48, backend и frontend PR
    • Depends on: 4.2, 4.3, 4.4, 4.5

5. Specs: G4 и завершение change

  • 5.1 [specs] Verification: запись G4 в process-record с evidence задач 1–4 (descoped, см. process-record)

    • Covers: N/A — gate протокол
    • Source: instruction process-record (Readiness Checklist, Gate Log)
    • Verification: npx -y @fission-ai/openspec@1.13.1 validate account-top-positions --store wealth-ledger-specs --strict valid; git diff --check чист; строка G4 содержит ссылки на PR, отчёты 3.1–3.3 и решение владельца
    • Depends on: 3.1, 3.2, 3.3, 3.4, 4.6
  • 5.2 [specs] Verification: native archive change после G4 и PR в develop со ссылкой на WealthLedger/main#48

    • Covers: N/A — native sync/archive выполняет CLI, canonical specs вручную не правятся
    • Source: schema wealth-ledger-sdd-lab apply/archive instruction; .ai/conventions/engineering-workflow.md «Merge → Issue Closed»
    • Verification: npx -y @fission-ai/openspec@1.13.1 archive account-top-positions --store wealth-ledger-specs завершён; openspec validate --all --strict valid; PR specs открыт; Issue main#48 закрывается после merge всех PR
    • Depends on: 5.1