# extend-instrument-logo-to-bonds — Design ## Context Данные уже готовы: `logoName` приходит в `PortfolioPositionDto` для позиций любого типа (passthrough из T-Bank metadata, без персистенции), фронтенд строит CDN-URL через `instrumentLogoUrl` и рендерит логотип компонентом `PositionLogo` (40px, фолбэк первой буквой через `onError`). Ограничение «только акции» существует в одном месте представления страницы счёта. Осложнение: локальный проп `stock` на `PositionSection`/`PositionCard` управляет сразу тремя аспектами — одноколоночной раскладкой grid, рендером логотипа и форматом абсолютного дохода (знаковое значение против текстовой подписи). Паритет облигаций означает, что все три аспекта применяются к секции `BOND` целиком; менять их по отдельности не нужно. Ограничения репозитория: FSD-границы и public API (dependency-cruiser, Knip), только typed client поверх generated OpenAPI, i18n через локали, gate `npm ci && npm run verify` под Node 22. ## Goals / Non-Goals **Goals:** - Секции `BOND` получают полное представление плиток `STOCK`: одна карточка в ряду, визуальный логотип с буква-фолбэком, знаковый доход без подписи. - Минимальный диф в слое представления страницы счёта; переиспользование существующих `instrumentLogoUrl` и `PositionLogo` без изменений. - Тесты, зеркалирующие существующие сценарии акций для облигаций. **Non-Goals:** - Логотипы для общей секции иных типов (фондов, валют и т.д.) — гейт остаётся allowlist по типам секций. - Изменения backend, API-контракта, generated types и OpenAPI snapshot. - Экстракция `PositionLogo`/`instrumentLogoUrl` в entities ради переиспользования — потребителя по-прежнему один экран. - Логотипы на странице upcoming events; понятие «эмитент» в модели. ## Decisions **D1. Разъединить смысл флага, сохранив один проп.** Проп `stock` переименовывается в презентационный термин (например, `wide`), отражающий «широкая плитка во всю строку + логотип + знаковый доход». Секции `STOCK` и `BOND` передают его, общая секция иных типов — нет. *Alternative:* два независимых пропа (`onePerRow`, `withLogo`). Отклонено: сегодня они всегда движутся вместе; разъединять, пока не появился тип, которому нужен только один аспект, — спекуляция. **D2. Логотип без изменений.** `PositionLogo` и `instrumentLogoUrl` реиспользуются как есть, включая `key={logoSrc ?? "no-logo"}` для сброса failed-state между карточками и CDN-размер 160 для 40px аватара. Новых настроек размера/радиуса не вводится. **D3. Формат доходности следует за флагом.** Знаковый формат без подписи применяется к `STOCK` и `BOND`, текстовая подпись остаётся только у секции иных типов — как прямое следствие D1, без отдельной ветки по типу. **D4. Тесты повторяют паттерны акций.** В тестах страницы счёта добавляются случаи: облигация с `logoName` рендерит изображение с брендовым URL; облигация без `logoName` — буква-фолбэк без img; знаковый доход облигации без текстовой подписи; одноколоночная секция облигаций. Существующие тесты секции иных типов служат регрессионной сеткой неизменённого пути. ## Delivery Map | Area | Status | Architectural outcome or N/A reason | | --- | --- | --- | | frontend | `affected` | Представление страницы счёта: единый презентационный флаг для секций `STOCK` и `BOND`; логотип и знаковый доход на карточках облигаций; обновлённые unit/component тесты | | backend | `N/A` | `logoName` уже возвращается для всех типов позиций; контракт Portfolio и зависимости не меняются | | specs | `affected` | Утверждённые Delta Specs синхронизируются в главную спеку `portfolio-view` при archive после реализации | | docs | `affected` | As-built: `docs/features/portfolio-view.md` отражает паритет облигаций; глоссарий не меняется (термин Instrument Logo уже типо-агностичен) | ### Contract Gate Decision | Gate | Status | Reason | | --- | --- | --- | | runtime-openapi | `N/A` | API-контракт не меняется ни на одной стороне: backend вне scope, generated types не обновляются | ## Risks / Trade-offs - **Редкие бренды у облигаций.** Брендовые данные T-Bank для облигаций встречаются реже, чем у акций, поэтому буква-фолбэк будет видна чаще. Mitigation: фолбэк — штатное, уже протестированное поведение; спецификация явно допускает оба исхода. - **Связанность трёх аспектов представления.** Общий флаг означает: нельзя включить логотип, не сделав плитку широкой. Осознанный trade-off ради минимального дифа; точка разъединения обозначена в D1. - **Визуальная регрессия других секций.** Путь секции иных типов не меняется (ветка по умолчанию того же флага); риск ограничен одним файлом представления и покрыт существующими тестами. ## Migration Plan Поставка одним изменением frontend без флагов миграции: изменение чисто презентационное, состояние и данные не затрагиваются. Rollback — revert коммита frontend. Порядок: имплементация → `npm ci && npm run verify` → semantic verification против Proposal/Delta Specs/Design → as-built docs → workspace gate `verify-workspace-change.sh` → archive.