# ADR-061: Optional badge/subtitle + self-healing docker entrypoint (PR#144) ## Статус Accepted (2026-07-29) ## Контекст PR#134 (ADR-060) ввёл SVG template renderer для on-brand covers. При тестовом рендере выявлены три проблемы: 1. **Пустой badge рисует квадрат** — `renderSlotBackground()` вызывался для каждого слота с bg-спекой ДО проверки контента. Cover без `--slots badge=...` получал пустой `` в районе (780, 780). 2. **Subtitle оставляет пустой ``** — `{{subtitle}}` заменялся на пустую строку безусловно, но `` элемент оставался в DOM. Большинство cover'ов — title-only. 3. **Fresh clone = broken** — `node_modules/` (sharp, lucide-static) gitignored, Dockerfile не устанавливал deps для draw-image. После `docker compose up` → `Cannot find module 'sharp'`. ## Решение 1. **Slot bg conditional** — `renderSlotBackground()` перенесён внутрь `if (value)` блока в `buildSvg()`. bg/border рендерится ТОЛЬКО если слот заполнен контентом. 2. **Subtitle conditional** — если `args.subtitle` falsy, regex `/[^\n]*\{\{subtitle\}\}[^\n]*\n?/g` удаляет всю `` строку с `{{subtitle}}` из шаблона ДО подстановки. При наличии subtitle — обычная подстановка `escapeXml(args.subtitle)`. 3. **Self-healing entrypoint** — `docker-entrypoint.sh` (POSIX `#!/bin/sh`) проверяет `node_modules/sharp`, при отсутствии запускает `npm ci --prefix` (continue-on-error: WARN в логах, контейнер стартует), затем `exec opencode "$@"`. Dockerfile `ENTRYPOINT` → entrypoint shim. `.dockerignore` исключает `app_data/`, `.git`, `**/node_modules` из build context. ## Альтернативы - **Install draw-image deps в Dockerfile (RUN npm ci)** — отвергнуто: node_modules gitignored, COPY не сработает; `npm ci` в build-time требует COPY package.json + package-lock.json из `.opencode/draw-image/`, усложняет Dockerfile. Self-healing entrypoint — одноразовая задержка при первом старте, потом skip. - **Pre-build hook (postinstall sync-lucide в Dockerfile)** — уже работает через entrypoint: `npm ci` запускает postinstall (`sync-lucide.mjs`), восстанавливает 2007 иконок. - **Удалить `` subtitle через DOM-парсер (xmldom)** — overkill для одной строки. Regex по строке — достаточно, шаблон контролируемый. - **`set -euo pipefail` в entrypoint** — отвергнуто: `npm ci` failure (нет сети) должен быть continue-on-error, иначе контейнер не стартует. POSIX `#!/bin/sh` для портативности в slim-образах.