GR-1: Global Rule about preview-страницы
Каждый артефакт системы (постер, карусель, explainer-видео) автоматически сопровождается self-contained HTML preview с payload-сайдбаром. Это контракт между генератором и тем, кто будет ревьюить результат.
Что такое GR-1
Это правило родилось из одного простого факта: agency-review не работает без provenance. Когда reviewer открывает 6 PNG-постеров без контекста — он не знает, какой промпт это дал, какие референсы использовались, какая модель, в какой версии бренда. Он не может ни одобрить, ни сказать что починить. Поэтому каждый артефакт должен сам себя «объяснять».
Зачем это нужно
| Сценарий | Без GR-1 | С GR-1 |
|---|---|---|
| Клиент просит «доработать постер #3» | Лезешь в БД, выясняешь какой это poster_id, тянешь payload, восстанавливаешь промпт | Открываешь preview.html, в сайдбаре весь payload — кидаешь в refine.py |
| QA отлетел на 3-м слайде карусели | Гадаешь — это плохой промпт или плохой бренд-цвет | В preview видишь visual_hint, brand-палитру и сразу понимаешь причину |
| Отчёт по стоимости батча | Считаешь по логам | В каждом preview — model + cost; легко сагрегировать |
| «Покажи клиенту что мы наклепали» | ZIP с PNG и отдельный текстовый файл | Один HTML-файл, отправляешь в чат / email |
| Дебаг «почему этот explainer длится 3:14 а не 3:00» | Считаешь руками длительность каждой сцены из плана | В preview по сцене — duration_seconds, видно где растянулось |
Реализации в трёх скиллах
creative-poster · present.py
Файл: ~/.claude/skills/creative-poster/scripts/present.py.
Что внутри HTML:
- Stage — большой постер по центру (PNG embedded как base64)
- Lightbox — клик на постер → fullscreen с ESC для выхода
- Sidebar payload:
- Production meta: renderer (gemini-3-pro-image-preview), aspect (4:5 / 9:16 / 1:1), preset, cost, timestamp, поколение
- Brand context: имя бренда, color palette с swatches, fonts, voice tone
- Brief: offer / bullets / profits / target audience
- Prompt payload: финальный prompt, который ушёл в Gemini Image
- References: thumbnail каждого reference-фото, использованного multi-shot
- QA: score, suggested_fix (если был), число refine-итераций
Файл лежит рядом с PNG, например:
~/video-projects/apparatus/posters/2026-05-16/poster_a_4x5.png ~/video-projects/apparatus/posters/2026-05-16/poster_a_4x5.html ← preview
creative-carousel · present.py
Файл: ~/.claude/skills/creative-carousel/scripts/present.py.
Что внутри HTML:
- Stage — горизонтальный scroll-snap row со всеми слайдами (Instagram swipe feel)
- Thumbnail strip внизу — клик переключает активный слайд
- Lightbox — клик на слайд → fullscreen с ←/→ keyboard navigation
- Sidebar payload:
- Production meta: renderer (remotion-still), aspect (4:5 / 1:1), template (product_launch и т.д.), slide count, project
- Brand context: имя, palette swatches, fonts, voice tone
- Topic / brief, который пошёл в plan.py
- Per-slide payload (меняется при переключении слайда):
- slide_n, type (hook/problem/...)
- headline / subhead / supporting_text
- visual_hint (то что plan.py выдал)
- bullets / chart_data (если применимо)
Файл лежит рядом со слайдами, например:
~/video-projects/apparatus/carousel/2026-05-16/slide_01.png ~/video-projects/apparatus/carousel/2026-05-16/slide_02.png ~/video-projects/apparatus/carousel/2026-05-16/plan.json ~/video-projects/apparatus/carousel/2026-05-16/preview.html ← preview
creative-explainer-video · present.py
Файл: ~/.claude/skills/creative-explainer-video/scripts/present.py.
Что внутри HTML:
- Video player — embedded mp4 (через
<video>tag, base64 если файл компактный, иначе link на mp4 рядом) - Scene timeline strip снизу — кликабельный, jump к моменту начала сцены через player.currentTime
- Sidebar payload:
- Production meta: renderer (remotion-render), aspect (9:16 / 1:1 / 16:9), project, total duration, scene count, наличие bg-music, cost (Gemini + ElevenLabs)
- Brand context: имя, palette swatches, fonts, VO voice_id
- Topic / brief, который пошёл в plan.py
- Per-scene payload (меняется при клике на timeline):
- scene_n, type (title_card / bullet_list / ...)
- vo_text — текст озвучки целиком
- on_screen_text
- visual_hint
- duration_seconds (заполнено после vo.py)
- asset_ref (если есть)
- Assets used: список фото / screen-records / bg-music
- References from brand.yaml
Файл лежит рядом с mp4:
~/Desktop/apparatus_explainer.mp4 ~/Desktop/apparatus_explainer.html ← preview
Авто-вызов
Каждый present.py автоматически зовётся в конце своего генератора:
| Генератор | Когда зовёт present.py |
|---|---|
creative-poster · gen.py | Сразу после успешного сохранения PNG |
creative-poster · multi_aspect.py | После outpaint каждого аспекта |
creative-carousel · render_slides.py | После рендера всех слайдов |
creative-explainer-video · render.py | После завершения Remotion render + ffmpeg mix |
Можно отключить флагом --no-present если не нужно (например когда orchestrator делает батч и не хочет открывать N окон в браузере). По дефолту — открыт всегда.
Что лежит в Sidebar (универсальная структура)
| Секция | Поля |
|---|---|
| Production meta | renderer / aspect / template / cost / timestamp / project / generation_seed |
| Brand context | имя бренда / palette swatches с hex / fonts (headline/body/mono) / voice tone / VO voice_id (для explainer) |
| Topic / brief | исходный topic + offer/bullets/profits |
| Per-item payload | prompt (для poster) / per-slide visual_hint (carousel) / per-scene vo_text + visual_hint (explainer) |
| References | thumbnails использованных reference-фото (multi-shot для poster, product-photo для carousel/explainer) |
| QA / refine history | score, suggested_fix, число итераций (только для poster — другие не QA-ятся) |
Coverage по скиллам
| Скилл | present.py | Что в HTML |
|---|---|---|
| creative-poster | есть | single poster + lightbox + payload |
| creative-carousel | есть | multi-slide scroll-snap + thumbnails + lightbox + per-slide payload |
| creative-explainer-video | есть | embedded mp4 + clickable scene timeline + per-scene payload |
| video-orchestrator | TODO | планируется: финальный mp4 + timeline клипов + per-clip payload |
Self-contained = offline-friendly
Один из критических контрактов GR-1: HTML должен работать без сетевого доступа. Это значит:
- Все изображения embedded как
data:image/png;base64,... - Все шрифты либо system fonts, либо тоже base64
- CSS inline в
<style>, никаких внешних link rel="stylesheet" - JS inline в
<script>, без CDN - Video — обычно embedded base64 если файл <30MB; для больших — link на mp4 рядом (но тогда HTML и mp4 должны переезжать вместе)
Как добавить GR-1 в новый скилл
Если ты пишешь новый creative-* скилл, который выдаёт боевые артефакты, ему нужен свой present.py. Минимальный контракт:
- Принимает на вход путь к артефакту + JSON-план (если есть)
- Собирает payload из артефакта, плана и meta (project.yaml, brand.yaml, БД)
- Генерирует HTML с inline CSS/JS + embedded media
- Пишет рядом с артефактом
- Опционально открывает (
--openчерезwebbrowser.open()на macOS)
Стандартный CLI:
python <skill>/scripts/present.py \ --plan plan.json \ --output-dir <artifacts>/ \ --output ~/Desktop/<name>.html \ [--open]
creative-poster/scripts/present.py и адаптируй. Все три существующих present.py намеренно похожи структурно, чтобы reviewer переключался между ними без когнитивного оверхеда.