* fix(cover): restore sub-icon slot in cover.svg * docs(cover): add ADR-089 and handoff for PR #201 --------- Co-authored-by: opencode-agent <agent@opencode.local>
31 lines
5.6 KiB
Markdown
31 lines
5.6 KiB
Markdown
# ADR-089: Restore sub-icon slot in production cover.svg (PR#201)
|
||
|
||
## Статус
|
||
Accepted (2026-07-31)
|
||
|
||
## Контекст
|
||
PR#197 (ADR-087) упростил продакшн-шаблон `templates/cover.svg` до единственного `icon`-слота 520×520 + `{{title}}` y=830, удалив slot-комментарии `sub-icon`, `badge` и subtitle-`<text>`. Это было демонстрацией отвязки тестов от продакшн-шаблона (тесты переехали на `tests/fixtures/cover.svg`). Удалённые слоты в продакшн-рендере тогда не использовались, и `assets/cover.png` остался побайтово идентичным.
|
||
|
||
Однако движок `buildSvg` итерирует по слотам, **объявлённым в шаблоне** (`slot-parser` читает HTML-комментарии из шаблона), а не по `args.slots`, переданным в вызов. Поэтому после PR#197 переданный `sub-icon=...` молча отбрасывался — слот не объявлен в шаблоне → рендерер его не видит.
|
||
|
||
Команда `/cover` (PR#199, ADR-088) документирует `sub-icon` как optional-слот и передаёт его в `draw-image` (пример: `sub-icon=opencode`). Возникла рассогласованность: команда обещает `sub-icon`, рендерер его игнорирует. Сценарий «icon + sub-icon + title» (например, иконка фичи + логотип OpenCode) не работал.
|
||
|
||
Условный рендер слотов (PR#144, ADR-061) уже поддерживает опциональность: передан `sub-icon` → рендерится; не передан → слот пропускается. Это доказано fixture-тестами (`render.optional.test.ts` на `tests/fixtures/cover.svg`, где sub-icon присутствует). Движок поддерживал sub-icon всегда — не хватало только объявления слота в продакшн-шаблоне.
|
||
|
||
## Решение
|
||
1. **Восстановлена ровно одна строка** slot-комментария `sub-icon` в `templates/cover.svg`:
|
||
`<!-- slot: name=sub-icon, x=412, y=600, w=200, h=200, fit=contain, recolor=accent -->`
|
||
(та самая, что удалил PR#197). Изменений в коде движка (`src/render.ts`) и команде `/cover` нет.
|
||
2. **Только `sub-icon`** — `badge` и `subtitle` остаются удалёнными из продакшн-шаблона (вне scope PR#201). `/cover` (ADR-088) запрещает subtitle стандартом; badge не используется в Lime-стандарте.
|
||
3. **Условный рендер (PR#144) сохраняет одно-иконочный кейс побайтово идентичным**: `assets/cover.png` (`icon=opencode`, без `sub-icon`) → 18077 bytes, `cmp` → IDENTICAL. Рендер с двумя иконками (`icon=mic,sub-icon=opencode`) → 23171 bytes (больше, как ожидалось).
|
||
4. **Тесты не затронуты**: 58/58 проходят — тесты используют `tests/fixtures/cover.svg` (PR#197), а не продакшн-шаблон, поэтому изменение продакшн-шаблона их не ломает.
|
||
|
||
## Альтернативы
|
||
- **Не восстанавливать слот, убрать `sub-icon` из `/cover` (ADR-088)**: отвергнуто — `sub-icon` — полезный сценарий (иконка фичи + логотип проекта), и ADR-088 уже документирует его как optional. Убрать из команды = потерять функциональность, которую движок уже поддерживает.
|
||
- **Восстановить также `badge` и `subtitle`**: отвергнуто — вне scope issue #200. Lime-стандарт (ADR-088) запрещает subtitle; badge не используется. Восстановление мёртвых слотов вернёт проблему, которую решал PR#197.
|
||
- **Изменить `buildSvg` чтобы итерировать по `args.slots`, а не по шаблонным слотам**: отвергнуто — крупное архитектурное изменение движка ради одной строки. Текущая модель (шаблон объявляет слоты, рендерер их заполняет) чище и детерминированнее: шаблон = контракт на layout, args = данные.
|
||
|
||
## Последствия
|
||
- Частичный реверс ADR-087 (PR#197): продакшн `templates/cover.svg` снова объявляет `sub-icon` (наряду с `icon`). `badge` и `subtitle` остаются удалёнными — реверс точечный.
|
||
- Сценарий «icon + sub-icon» теперь работает в продакшн-рендере; одно-иконочный кейс побайтово идентичен (условный рендер PR#144).
|
||
- Тест-контракт (`tests/fixtures/cover.svg`) не изменён — продакшн и fixture снова расходятся по набору слотов (fixture имеет все 4 типа, продакшн — 2), но это намеренно: fixture тестирует движок, продакшн — конкретный бренд-стандарт.
|