{"id":24974,"plugin_id":"plugin_asdk_app_6a3293e129088191abf0875820e839da","kind":"skill","collection_source":null,"comparison_source":null,"observed_at":"2026-09-30T23:18:57.942Z","digest":"d5411861fbf6eef703a9d6e431798163f3271bc3e3f554d10344dea16a50f5e4","against":5377,"payload":{"name":"thumbnail-generation","description":"Create or revise finished YouTube/Instagram thumbnails and video cover images. Require a request to make or change the image; critique, readability analysis, title ideas and channel planning alone stay text. Named Higgsfield presets are resolved by higgsfield first: do not load this workflow merely because a slash token names a cover. For presets, load it only when the resolved instructions request it. Exclude generic images, complete videos, video edits and title cards within footage.\n","included_files":[{"relative_path":"agents/openai.yaml","size_in_bytes":259},{"relative_path":"references/text-overlay-bake.md","size_in_bytes":11385},{"relative_path":"references/thumbnail-frameworks.md","size_in_bytes":4063}],"skill_md_contents":"---\nname: thumbnail-generation\ndescription: >\n  Create or revise finished YouTube/Instagram thumbnails and video cover\n  images. Require a request to make or change the image; critique, readability\n  analysis, title ideas and channel planning alone stay text. Named Higgsfield\n  presets are resolved by higgsfield first: do not load this workflow merely\n  because a slash token names a cover. For presets, load it only when the\n  resolved instructions request it. Exclude generic images, complete videos,\n  video edits and title cards within footage.\n---\n\n## Activation analytics\n\nWhen using this skill for a new user request, call `track_skill_activation` once with `{\"skill_name\":\"thumbnail-generation\"}` at the earliest opportunity that preserves widget-first and exclusive-tool turns; defer to a later turn when required. Do not repeat for polling, retries, references, or continuation of the same request. If tracking is unavailable or fails, continue the task without retrying. Send only the skill name.\n\n# Thumbnail Generation\n\nThe full production pipeline for cinematic top-tier YouTube/Instagram\nthumbnails: **concept framework → casting → scene → 4K render → surgical tweaks → text**.\nDistilled from the Thumbnail Maker app build and live user feedback.\n\n## When to Use\n\nUse for a requested finished thumbnail/cover image or a revision to that image.\nReviewing readability, comparing concepts or writing titles without requesting\nan image stays an ordinary analysis/text task. Reference analysis below supports\nan already requested production task; it does not independently activate it.\n\nA named Higgsfield preset must be resolved through `higgsfield` first. Load this\nworkflow for that preset only if the returned instructions call for it; a cover\nword in the command does not imply this production pipeline.\n\nFor a qualifying image-production task, use this skill before `generate_image`.\nReference analysis (when the user attaches an example image) is done with YOUR OWN\nvision — look at the reference and extract the structured fields in the\n\"Reference analysis contract\" below. The reference itself is NEVER sent to the\ngeneration model and never enters `medias` — it only shapes the prompt through\nthose extracted fields.\n\n## OpenAI runtime contract\n\n- Use only tools exposed by the current OpenAI host and Higgsfield connector.\n- Use `ask_user_input` when that exact callable is exposed, `ask_user_input_v3` only\n  when that exact variant is exposed, otherwise ask one concise normal-chat question.\n  Never invent a legacy question tool.\n- For a ChatGPT attachment that must enter generation, call\n  `media_upload_and_confirm` once with the supplied file object and reuse its returned\n  `media_id`. A style-only reference thumbnail stays in the host's visual context and\n  is never uploaded or passed to generation.\n- Authorized HTTPS image URLs may be passed directly in `medias[].value`; the OpenAI\n  generation route imports and confirms them. Never invent a separate import tool.\n- One user-facing variant uses `generate_image`. Two or more distinct prompts use\n  `generate_image_batch` in groups of at most six, each item shaped\n  `{index,params:{...,count:1}}`. Wait with `jobs_wait` in groups of at most eight,\n  freeze completed indices, and display the final exact ledger once with\n  `show_generation_by_ids`.\n- Use `jobs_wait` for submitted batch jobs and use `models_get` or `models_search`\n  only when a locked model fails or its live contract must be checked. Do not reach for\n  tools outside the current OpenAI profile.\n- Run downloads and text baking only in `sandbox_exec`. For a sandbox-created PNG,\n  call `media_upload` before the producing command, PUT the file in that same command,\n  then call `media_confirm` only after HTTP 200. Never pass a sandbox path to\n  `media_upload_and_confirm`.\n- If a generation returns `unlim_choice`, no job was submitted. Ask its message and\n  resubmit unchanged with the user's selected `use_unlim` value; never choose for them.\n\n## EXECUTE — literal pipeline (follow IN ORDER; a step with IF fires only when its IF holds)\n\nPrompt language: assemble every generation prompt in ENGLISH (translate the user's scene\ndescription); baked TEXT strings stay verbatim in the user's language. Templates: copy library\nsentences exactly and fill every `<placeholder>` — a `<...>` slot never ships unfilled.\nPrecedence everywhere: explicit user ask > reference-extracted field > per-field default.\nEvery `generate_image` call takes ONE `params` object — `model`, `prompt`, `aspect_ratio`,\n`medias` AND model-specific settings (`resolution`, `quality`, …) are all keys of that same\n`params` object, never separate top-level fields.\n\n1. **Collect inputs** from the user message: scene text · face or character references (0–3) · reference\n   thumbnail (a style EXAMPLE, never a face source) · logo · headline text and whether to bake\n   it · ratio · emotions and variant count. Apply per-field defaults (below) to whatever is\n   missing. Then **pick the framework(s)** — read `references/thumbnail-frameworks.md`\n   from this installed skill: every\n   concept must open an information gap; brainstorm ≥5 options across frameworks and carry the\n   strongest (possibly a combination) into the prompt blocks.\n\n   **Text-in-generation gate (decide BEFORE generating):** the default is ALWAYS a CLEAN render\n   with NO text baked into the image. Bake text INTO the generation ONLY on an EXPLICIT user text\n   request — the user gives a headline/string to show, says \"add text\" / \"with a caption\", or\n   asks for a text-based framework BY NAME. A text-carrying framework (Social UI / News Clip /\n   Day badge / map callout) that is merely brainstormed or auto-picked while exploring frameworks\n   does NOT authorize baking — render that concept text-free, or ask first. Never infer text\n   intent from the topic or from the framework pick; when unsure, default to clean or ask once.\n\n   **EXCEPTION — faceless Phase-8b handoffs:** when the caller supplies the locked style key,\n   3–6 word hook title, render medium, `bake during generation:provider-first`, people/identity choice, and\n   variant count `one`, treat every supplied value as an answered, locked input. Do not reopen\n   match mode, character, text-bake, ratio, medium, or variant-count intake. Render exactly one\n   16:9 image with the hook in block 3's exact `TEXT` contract. Validate it character-for-character.\n   If provider text fails, render one new clean version of the same concept with the default\n   no-text block, then bake the exact hook through the deterministic recovery in step 9.\n   For a non-photoreal medium such as paper collage, flat motion graphics, storybook, or cutout:\n   - replace block 1's photoreal frame contract with `reproduce the exact medium of the style key`,\n     naming the supplied medium, materials, palette, and rendering verbatim and ending `NOT\n     photoreal, no real photography`;\n   - drop block 10's photographic lighting rig and block 11's glossy photoreal grade while keeping\n     the hero subject, safe zone, palette, and punchy feed-size legibility;\n   - translate recurring characters into the locked medium while preserving their exact design,\n     silhouette, wardrobe, colors, and facial identifiers; do not turn illustrated references into\n     photoreal faces.\n   A `photoreal` medium keeps the normal house frame, lighting, grade, and identity contracts.\n\n   **Character gate (MANDATORY — run this FIRST, before rendering ANYTHING; the face question is\n   first-class):** decide WHO is in frame; never assume, never silently substitute a stranger, and\n   NEVER generate a person as a periodic or silent default. **Reference-lock is the DEFAULT: whenever\n   a face photo OR a provided character image exists, ALWAYS upload it, attach its confirmed id as an\n   image reference (`image 1 = CHARACTER 1`, `image 2 = CHARACTER 2`, ...), and Identity-Lock it\n   (block 4). NEVER invent or substitute a new person or character when one was supplied.** If the\n   concept(s) will contain a person and NO face photo / character is attached, STOP and ASK ONCE, up\n   front: \"Do you want yourself (or a specific person) in the thumbnail? Send a face photo and I'll\n   lock the identity — or should it be a generated person, or people-free?\" In a BATCH (e.g. \"go\n   through all frameworks / render N\"), ask this ONCE before rendering the set whenever ANY chosen\n   framework is people-centric — do not proceed until it is answered. Then: a supplied face photo /\n   provided character → Identity Lock (block 4, up to 3 references); the user EXPLICITLY asks to\n   generate a person → a generated person described in prose (ONLY an explicit choice, NEVER the\n   silent default); no people → a subject-only framework (Landscape / Product / Graphical /\n   Map-Aerial). Never invent a specific identity and never skip a supplied character.\n\n   **Variant-count gate (decide BEFORE generating):** an exact variant count already supplied by\n   the user or calling workflow is an answered field — use it and do not ask again. Otherwise ask\n   once whether the user wants a single\n   thumbnail or a SET of variants of the same concept — offer ~4 by default (same concept, each\n   a different emotion and/or camera take), rendered as distinct requests (never one `count:N`\n   call). One thumbnail if they prefer. Variants = emotions × takes, hard cap 16\n   (see below).\n2. **IF a reference thumbnail is supplied:** before analysis, ask exactly once whether to\n   **Match this reference** (default: closely preserve its style, composition and subject)\n   or make a **Unique take** (use it only as loose inspiration). Use the runtime question\n   contract above and stop until answered. A previously stated \"match\" / \"inspired by\" intent is\n   already the answer and must not be re-asked. On Match, drive the prompt hard from the\n   reference; if it contains a real person, still require that person's supplied face photo\n   rather than generating a stranger. On Unique, carry only loose visual inspiration and\n   author a fresh subject/composition.\n\n   Then analyze the reference with your own vision to fill the \"Reference analysis\n   contract\" (below) — extract exactly those fields as STRICT JSON, then use them to\n   drive the prompt according to the settled match mode. Field→block mapping: brief→block 2 · subject→block 4 · elements→block 5 ·\n   location→block 7 · composition→block 8 · background→block 9 · split→step 5 split branch ·\n   emotion + emotion_detail→the Expression slot. The reference's `subject` prose attaches to\n   the photo characters positionally as their pose/action — it never ADDS a person; a\n   `person_count` above the number of attached photos becomes text-described extras ONLY if the\n   user asked for them. The reference is analyzed by eye ONLY — it NEVER enters any `medias`\n   and is NEVER sent to the generation model.\n3. **IF any face/character references or a 2D logo were supplied as ChatGPT attachments and do\n   not already have confirmed `media_id` values:** call `media_upload_and_confirm` once per\n   generation input. Preserve character ids in CHARACTER numbering order and record the logo\n   separately. Do not upload a style-only reference thumbnail and do not call `media_confirm`\n   after the combined helper. Otherwise skip this step.\n4. **IF a logo was uploaded AND the user wants it 3D:** submit the \"3D logo prompt\" (below) NOW\n   as one headless `generate_image_batch` item — `model:\"gpt_image_2\"`, `resolution:\"4k\"`,\n   `aspect_ratio:\"1:1\"`, `quality:\"high\"`, `count:1`, medias = the uploaded 2D logo id. Wait\n   on the returned indexed job with `jobs_wait`; the MAIN render on the 3D path uses its\n   completed job id as the logo media entry.\n5. **Assemble ONE prompt per variant** from \"House prompt structure\" blocks 1–11 in order.\n   - Include/omit rules: blocks 1 and 11 — always, except the non-photoreal faceless handoff drops\n     block 11 and substitutes its medium-locked block 1. 2 — if scene text or reference `brief`\n     exists. 3 — always the default no-text line; the TEXT / BAKED UI variant fires ONLY on an\n     EXPLICIT user text request, NEVER merely because a text-carrying framework was picked. 4 — if ANY person or creature is in frame:\n     photo-referenced people get an Identity Lock each; text-described people/creatures are\n     described here in prose (never in block 5) and their prose ALSO ends with\n     `Expression: <emotion phrase>` — the Expression slot exists per subject regardless of photo\n     reference. 5 — if signature props exist or the KEY-ELEMENTS default fires. 6 — if logo. 7 —\n     if a location is known. 8 and 9 — always (use the per-field default line when nothing is\n     known). 10 — if any person is in frame, except the non-photoreal faceless handoff drops it;\n     plain rig by default, the colored-rim variant ONLY when the USER names a rim color.\n   - `<ratio note>` = \"`<ratio>` aspect ratio\". For 9:16 append the tall-canvas clause; with no\n     people in frame say `the hero subject in the upper two-thirds` instead of `faces`.\n   - Expression slot = the PARENTHETICAL descriptor of the chosen emotion preset (shock →\n     `mouth open gasp, wide eyes`), or the user's custom phrase verbatim. Append the\n     reference's `emotion_detail` sentence ONLY on the variant(s) using the reference's own\n     emotion — user-overridden emotions use the preset parenthetical alone.\n   - **Split branch:** fires per the Trigger rule in \"Split frames\" (below) — layout asks only;\n     `X vs Y` as a SCENE stays one unified frame. When it fires, the split contract\n     replaces block 1 and counts as it.\n   - Variants = emotions × takes, hard cap 16; no person in frame → the Expression axis is\n     empty and variants = takes. Variants differ ONLY by the Expression phrase and/or one\n     ALTERNATE TAKE line.\n6. **Submit:** every variant gets its own distinct prompt and exactly one job. For one variant,\n   call `generate_image` so the result widget renders immediately. For two or more, call\n   `generate_image_batch` in groups of at most six with stable indices, then use `jobs_wait` and\n   one final `show_generation_by_ids`. NEVER use `count` to multiply a variant. `medias` lists one\n   entry per asset (characters first, then the logo) and is omitted when empty:\n\n```\ngenerate_image({ params: {\n  model: \"nano_banana_pro\", prompt: \"<variant prompt>\", aspect_ratio: \"<ratio>\",\n  resolution: \"4k\", medias: [ {value: \"<character-id-1>\", role: \"image\"},\n                              {value: \"<logo-id-or-3d-job-id>\", role: \"image\"} ] } })\n\ngenerate_image_batch({ requests: [\n  {index: 0, params: {model: \"nano_banana_pro\", prompt: \"<variant 0>\",\n   aspect_ratio: \"<ratio>\", resolution: \"4k\", count: 1, medias: [...]}}\n] })\n```\n\n   With 2+ medias the FIRST prompt line is the manifest:\n   `IMAGE REFERENCES: image 1 = CHARACTER 1 face reference; image 2 = brand logo.`\n7. **POST-RENDER CHECK on every image:** (a) IF face or character references exist — identity and\n   character design visibly match them; (b) baking NOT ordered → no stray text/watermark anywhere; (b2) baking ordered →\n   the rendered text matches the ordered string character-for-character; (c) the Expression\n   (when a subject has one) and the hero element still read at ~120px wide. For faceless\n   `provider-first`, a text mismatch does not enter the generic same-prompt retry loop: generate\n   one clean no-text recovery render of the same concept and continue to deterministic overlay in\n   step 9. Any other failure → re-render the SAME prompt (max 2 retries per variant); still failing\n   → report it honestly, never ship silently.\n8. **IF the user asks for tweaks:** use the \"Surgical tweaks\" prompts verbatim in a new\n   `generate_image` call on `seedream_v5_pro`, `resolution:\"2k\"`, i2i\n   `medias:[{value:\"<picked job_id>\", role:\"image\"}]`.\n   Submit error OR model absent from the catalog → retry ONCE on `seedream_v4_5` with\n   `quality:\"high\"`, same prompt. Each accepted output's job id feeds the next tweak.\n9. **Export and present every variant that passed step 7**; \"picked\" = the user's choice\n   (user doesn't choose → deliver all). No text supplied → deliver the clean renders as-is.\n   Headline text supplied and text was NOT explicitly baked into generation, or faceless\n   `provider-first` entered exact-text recovery → bake the\n   deterministic overlay into a flat PNG; a browser-only preview is not a deliverable:\n   - inspect the picked render and choose `top`, `bottom`, `left`, `right`, or `center` so the\n     headline occupies a free quarter and never covers the face;\n   - choose one Text policy style (`beast` default, `fire`, `neon-lime`, `clean-glass`, or\n     `marker`), and keep the headline to 2–6 words;\n   - call `media_upload({filename:\"thumbnail.png\",content_type:\"image/png\"})` BEFORE the\n     producing sandbox call;\n   - this reserve is stateful and single-use: record the first successful `media_id` and\n     `upload_url`, then reuse that exact pair. For one picked variant call `media_upload`\n     exactly once and `media_confirm` exactly once. Never reserve again to rename the file,\n     never request a second slot after a successful reserve, and never repeat a successful\n     confirmation;\n   - in ONE `sandbox_exec`, download the picked render and run the bundled native-resolution\n     canvas recipe, then verify and upload the result:\n     ```bash\n     set -e\n     curl -fL --retry 3 '<picked_result_url>' -o work/thumbnail_clean.png\n     printf '%s' '<headline_base64>' | base64 -d > work/headline.txt\n     TEXT_CASE=upper # use preserve only for faceless provider-first exact-text recovery\n     node \"$HF_WORKFLOWS/thumbnail-generation/scripts/bake_text_overlay.mjs\" \\\n       --image work/thumbnail_clean.png --text-file work/headline.txt --style beast \\\n       --position bottom --case \"$TEXT_CASE\" --out work/thumbnail.png\n     [ -s work/thumbnail.png ]\n     code=$(curl -sS -o /dev/null -w '%{http_code}' -X PUT \\\n       --upload-file work/thumbnail.png '<upload_url>')\n     [ \"$code\" = \"200\" ]\n     ```\n     Encode the exact headline as UTF-8 base64 before constructing the command; never interpolate\n     user text into shell syntax. The base64 placeholder contains only shell-safe characters.\n     Ordinary overlays keep the source recipe's `upper` default. Set `TEXT_CASE=preserve` only for\n     faceless provider-first recovery so the promised hook remains character-for-character exact.\n     Only after HTTP 200 call `media_confirm({type:\"image\",media_id:\"<media_id>\"})` once, and\n     deliver its confirmed hosted URL. Repeat the reserve → bake/PUT → confirm sequence per picked\n     variant, never within the same variant.\n\n### PRE-SUBMIT CHECKLIST (verify before every generate call; any NO → fix the prompt first)\n\n- [ ] Right model for the call: main render `nano_banana_pro` + `resolution:\"4k\"` · tweak →\n      seedream per step 8 · 3D logo → `gpt_image_2` per step 4\n- [ ] Block 1 (or its Split / non-photoreal faceless substitution) is the FIRST prompt block;\n      GRADE is the LAST when block 11 applies, while a non-photoreal faceless handoff omits it\n- [ ] Person in frame → LIGHTING rig present (the selected plain/colored variant, verbatim) +\n      an Identity Lock per face photo, except a non-photoreal faceless handoff uses its supplied\n      medium and character-design lock with no photographic lighting block\n- [ ] Reference thumbnail NOT in `medias`\n- [ ] Each ordinary call or batch request renders exactly ONE variant; batch `count` is `1`\n- [ ] Baking not ordered → the prompt contains `No text, no readable UI labels, no watermark.`\n\n### Per-field defaults (apply to ANY field both the user and the reference left empty)\n\n- ratio `16:9` · takes 1 · emotion `shock` when a person is in frame; no person → the Expression\n  axis is empty and variants = takes\n- BACKGROUND: `bold, vivid saturated color-field gradient with punchy high-contrast tones matching the subject's palette, soft\n  vignette, edge falloff`\n- COMPOSITION: `the subject rendered LARGE and dominant — chest-up / medium-close, filling ~40–60% of the frame, pushed to the foreground on a power third, camera at eye level, strong separation from the background so the subject pops, shallow depth with a foreground accent element`\n- KEY ELEMENTS: the most concrete depictable noun of the user's topic (your judgment),\n  oversized, flying toward camera — omit when it would duplicate the SUBJECT\n- LOCATION: omit the block\n\n## Thumbnail frameworks (the concept layer — pick BEFORE assembling the prompt)\n\nThe \"what to depict\" layer lives in a reference: the 16 engaging thumbnail frameworks, the\ninformation-gap principle, combining, and the truthfulness law. Load it on demand and pick the\nframework(s) in EXECUTE step 1:\n\n`references/thumbnail-frameworks.md` in this installed skill\n\nMost frameworks map straight onto the house-structure blocks below; the ones needing readable\nin-image text use the BAKED UI clause in the Text contract (block 3).\n\n## Model routing\n\n| Task | Model | Settings |\n|---|---|---|\n| Main thumbnail render | `nano_banana_pro` (Nano Banana Pro — Google's ultimate-quality tier; NOT `nano_banana_2`, which is the faster \"Flash\"/base tier) | `resolution: '4k'`, ALWAYS 4K (the model default is 1k — pass `4k` explicitly); batch via separate prompts, not `count` |\n| 3D logo from a flat 2D logo | `gpt_image_2` | `quality: 'high'`, `resolution: '4k'`, `aspect_ratio: '1:1'` |\n| Surgical edits on a finished render (emotion/background/colors) | `seedream_v5_pro` (top Seedream tier; **paid plans BASIC+ only**) | `resolution: '2k'`; i2i chained off the finished render; on a free plan or a catalog miss fall back to `seedream_v4_5` (`quality: 'high'` ≈ v5 2k) |\n\n**Unlim clash — 4K is normally not covered by an unlimited grant.** Pass `use_unlim:true`\nonly when the user explicitly asks. If the request returns `unlim_choice` or a typed uncovered\nconfiguration, ask whether to use the covered resolution or keep 4K on credits, then resubmit the\nunchanged creative request with that answer. Never silently downgrade and never silently charge.\n\n**Aspect ratios (always 4K):** 16:9 (YouTube), 4:3, 9:16 (Shorts 3072×5504), 4:5 (Instagram 3712×4608 — native on Nano Banana only; Seedream has NO 4:5, downgrade tweaks to 3:4 and disclose). Multi-reference submits: up to 14 images; prepend an \"IMAGE REFERENCES:\" manifest to the prompt numbering each image and its role.\n\n## House prompt structure (assemble in THIS order)\n\n1. **Frame contract** — `Bold, punchy YouTube-thumbnail composite — poster-grade, photoreal and high-impact, NOT a muted cinematic movie still, <ratio note>, single unified frame — no split-screen, no diagonal divide, everything blends smoothly and organically across the same continuous shot.` For 9:16 add `subject framing adapted to the tall canvas, faces in the upper two-thirds`. Framework 8 \"Graphical Representation\" replaces this with a clean diagram/graphic brief and drops the photoreal + lighting-rig blocks. A non-photoreal faceless Phase-8b handoff makes the same replacement using its supplied render medium and also drops the glossy grade.\n2. **Scene brief** (if the user described exact content): `SCENE BRIEF (must be depicted exactly): <text>.`\n3. **Text contract** — default: `No text, no readable UI labels, no watermark.` Only when baked text is explicitly wanted: `TEXT: bold thumbnail headline text baked into the image, reading exactly \"<TEXT>\" — massive, ultra-legible sans-serif with a clean outline/glow treatment, placed where it never covers the subject's face. No other text, no watermark.` When the user EXPLICITLY asked for a text-based framework that carries an in-image UI element (Social UI, News Clip, Day badge, Map/Aerial callout — see the frameworks reference), add instead: `BAKED UI: a <generic chat bubble / DM row / star-review card / breaking-news lower-third / DAY N badge / map callout label> reading exactly \"<short text>\", clean generic platform styling — NO real brand name, app name or network logo. Keep the text short and truthful to the video.` A text-carrying framework that was only auto-picked (not explicitly requested) renders text-free instead. This is the ONLY sanctioned readable text besides the headline; every other prop stays text-free and wordmark-free.\n4. **SUBJECT(s)** — see Identity Lock below. Up to 3 characters; photo characters map positionally onto attached face references. **Render the subject LARGE and dominant — the clear hero, filling roughly 40–60% of the frame, chest-up or medium-close, pushed to the foreground and cleanly separated from the background; NEVER a small subject stuck in the lower third, never a distant video-frame look.** End with `All faces crisply sharp as the anchors of the shot.`\n5. **KEY ELEMENTS** — signature props/effects that make it pop.\n6. **LOGO** (if any) — 2D: `the attached logo placed into the composition EXACTLY as provided — keep its shapes, colors and proportions untouched, clean 2D placement at a strong focal position, subtle drop shadow for separation, never covering the subject's face.` 3D: `the attached 3D logo render integrated into the scene as a physical volumetric object — glossy dimensional material, catching the scene's key light and rim light, casting a soft contact shadow, composited at a strong focal position without covering the subject's face.`\n7. **LOCATION** — place, time of day, weather, atmosphere.\n8. **COMPOSITION** — subject LARGE and foreground-dominant (fills ~40–60% of the frame, chest-up / medium-close) on a power third, strong subject-vs-background separation so the subject pops off the background; scale hierarchy, camera angle, depth layering.\n9. **BACKGROUND TREATMENT (blended, not divided)** — bold saturated color field, vivid punchy gradients, strong color contrast, texture, blur, edge falloff.\n10. **LIGHTING (the YouTube rig — mandatory on people)** — `signature YouTube thumbnail lighting rig on the subject — a strong KEY LIGHT sculpting the face with crisp highlights and controlled falloff, a soft dreamy DREAM LIGHT fill lifting the shadows with a subtle cinematic glow, and a defined BACK LIGHT + HAIR LIGHT tracing a clean bright rim along the hair, shoulders and silhouette, separating the subject sharply from the background.` Colored rim variant: replace last clause with `a defined <color> BACK LIGHT + HAIR LIGHT tracing a vivid colored rim ... with a subtle matching glow.` Rim palette: Ice Blue `#4DA6FF` \"electric ice-blue\", Neon Magenta `#FF3DBE` \"hot neon magenta\", Toxic Lime `#C8FF2E` \"toxic neon lime\", Amber Gold `#FFB63D` \"warm amber-gold\", Pure White. Key/fill NEVER change color — only back+hair.\n11. **GRADE** — `vivid high-impact color grade, punchy high contrast, bright clean exposure, rich saturated colors that pop off the screen, deep blacks and bright highlights, crisp and glossy, poster-punchy, cohesive as one image. <ratio>.` Dial back to a restrained / soft / low-contrast grade ONLY on an explicit calm / premium / muted / aesthetic request.\n\n## Identity Lock (anti face-drift — REQUIRED per photo-referenced person)\n\nSoft phrasing (\"keep face and identity exact\") is NOT enough — models drift. Per character with a face photo emit:\n\n> CHARACTER N: the person from attached face reference #K — IDENTITY LOCK: reproduce this exact person with a photographic identity match — same bone structure, eye shape, nose, lips, jawline, skin tone, hairline and hair texture as the reference photo. Do NOT beautify, do NOT average with other faces, do NOT restyle the face; it must be recognizably the same person at a glance. Expression: `<emotion phrase>`.\n\nDrift still happens occasionally (stochastic) — the recovery is re-render.\n\n## Emotion casting (11 presets)\n\n**shock** (mouth open gasp, wide eyes), **hype** (ecstatic grin, blazing eyes), **fear** (terrified stare, frozen breath), **confusion** (one brow raised, puzzled), **determination** (locked jaw, laser focus), **smug** (knowing smirk), **charisma** (calm magnetic gaze, relaxed brows, faintest composed half-smile — charismatic leading-man poise, confident but never aggressive; the calm positive option — \"determination\" alone reads too harsh), **disgust** (recoiling grimace), **awe** (jaw dropped, glittering wonder), **rage** (bared teeth fury), **laugh** (head back).\n\nThe emotion phrase used in Expression slots = the preset's PARENTHETICAL descriptor. A custom user phrase replaces the preset phrase verbatim (`Expression: <custom>`). When the user gives an emotion COUNT without naming them, take the first N of this ladder: shock → hype → rage → awe → laugh → fear → smug → charisma → confusion → determination → disgust. When offering emotion choices interactively, always include an Other/custom option.\n\n## Takes per emotion (camera variations)\n\nTake 1 = designed framing (no modifier). Takes 2–4 append one line each:\n\n- `ALTERNATE TAKE: reframe as a low-angle hero shot — camera below eye level looking up, the subject towering with extra dominance, background perspective stretching upward, same scene and lighting.`\n- `ALTERNATE TAKE: extreme close-up punch-in — the face and expression dominate more than half the frame, background compressed into soft bokeh context, same scene and lighting.`\n- `ALTERNATE TAKE: wider dynamic shot with a subtle dutch tilt — more of the environment visible, subject anchored off-center on a power third, stronger motion energy sweeping the frame, same scene and lighting.`\n\nTotal variants = emotions × takes (hard cap 16). Each variant = its own ordinary call or indexed\nbatch request with a distinct prompt (never one `count:N` submit).\n\n## Split frames\n\nTrigger (EXECUTE step 5): fires ONLY when the user asks for a split/panel LAYOUT — \"split\",\n\"before/after\", \"versus screen\", \"side by side\" — or the reference analysis\nreturned `split=true`. `X vs Y` as a SCENE means one unified frame with both subjects — NO\nsplit. N = the number of contenders/states/facets named (before/after and versus default to 2);\nN=2 → `halves`, N≥3 → `vertical panels`. The split contract REPLACES block 1 (the substitution\ncounts as block 1) and carries `<ratio note>` and the 9:16 tall-canvas clause exactly as\nblock 1 would.\n\nReplace block 1 with: `SPLIT-FRAME thumbnail, <ratio note>: the frame divided into N\n<halves|vertical panels> by clean bold seams, each panel its own complete mini-scene, unified\npremium grade across all panels.` — then append exactly ONE mode sentence:\n\n- **plain**: `Each panel shows one facet of the story: <panel 1: desc; panel 2: desc; ...>.`\n- **before/after**: `LEFT panel: the BEFORE state — <desc>. RIGHT panel: the AFTER state —\n  <desc>. Maximum visual contrast between the two states of the same transformation.`\n- **versus**: `Each panel presents one contender lit and framed like a fighter poster:\n  <panel 1: contender A desc; panel 2: contender B desc>. Equal visual weight, confrontation\n  energy across the seam.`\n- **custom**: `<the user's panel-by-panel description>.`\n\nALWAYS append: `No labels, no captions, no words, no numbers on or between the panels — the\ncomparison reads purely visually. All panels graded as one premium image.`\n\n## Text policy\n\nDefault: NO text in the image — deliver the clean render; headline text belongs in a crisp typographic overlay layered on top (zero generation credits, always legible). Bake text into the generation ONLY when the user explicitly asks (then use the TEXT block from the house structure) — generative type is the fallback, not the default. The one workflow exception is a faceless Phase-8b `provider-first` handoff: use its locked exact `TEXT` contract in the first provider render, validate the rendered characters, and use the clean-render deterministic-overlay recovery above only when that text mismatches. When the user EXPLICITLY asks for a text-based framework (Social UI / News Clip / Day badge / map callout), that in-image text uses the BAKED UI clause (block 3) — short, truthful, brand-generic. Never bake it just because such a framework was auto-picked while exploring.\n\n5 proven overlay styles (art direction for whatever surface renders the text): **Beast** (Anton white + heavy black stroke + drop shadow), **Fire** (yellow→orange→red gradient + dark stroke + warm glow), **Neon Lime** (`#D4FF3F` + lime glow), **Clean Glass** (Inter 800 on frosted blur pill), **Marker** (black Anton on lime line-boxes).\n\nThe full HTML/CSS + canvas-bake recipe for these 5 styles — exact stroke/shadow/gradient values,\nthe critical `paint-order: stroke fill`, font-load-before-draw, and 4K export — lives in\n`references/text-overlay-bake.md` in this installed skill. The executable implementation is\npreinstalled at `$HF_WORKFLOWS/thumbnail-generation/scripts/bake_text_overlay.mjs`; use it for\nthe default post-generation overlay. Baking text INTO generation remains the explicit-ask\nfallback except for the locked faceless Phase-8b provider-first path.\n\n## Surgical tweaks on a finished render (Seedream i2i)\n\nFeed the FINISHED render back as the i2i input (pass the completed job's id as `value` in `medias`). Every tweak prompt must state everything else stays pixel-faithful.\n\n- **Emotion swap:** `Change ONLY the person's facial expression ... to: <phrase>. Keep identity, face structure, hair, pose, body, clothing, logo, background, lighting and composition EXACTLY unchanged, pixel-faithful — pure expression swap. Keep the YouTube thumbnail lighting rig intact.`\n- **Background swap:** `Replace ONLY the background with: <desc>. Keep subject, face, identity, pose, clothing, logo and all foreground elements EXACTLY unchanged. Rebuild the lighting wrap around the subject so the new background's light direction, color and rim light read naturally — keep the YouTube rig.`\n- **Background recolor:** `Shift ONLY the background color palette to dominant <color> tones. Keep the background's structure, content and depth exactly — only recolor. Rebuild the subtle ambient color wrap on the subject's edges but keep key light and fill on the face unchanged.`\n- **Rim light recolor:** `Change ONLY the back light / hair light (rim light) color on the subject to <phrase> — the bright edge tracing hair, shoulders, silhouette. Do NOT change key light or fill; do NOT change background, pose, identity, clothing, logo, composition.`\n\nTweaks chain — each output becomes the new picked source.\n\n## 3D logo prompt (GPT Image 2)\n\n> Transform the attached 2D logo into a premium 3D logo render: extrude the exact logo shapes into glossy dimensional volumes, keep every letterform, proportion and brand color EXACT, high-end CGI product-render finish with soft studio reflections, subtle bevels, crisp edges, floating on a clean dark neutral studio background with a soft contact shadow. Centered, generous margins, no extra text, no watermark.\n\nRun it in the background, don't block the flow; the final render waits for it.\n\n## Reference analysis contract (vision → scene)\n\nExtraction is done with YOUR OWN vision — look at the attached reference thumbnail and produce\nSTRICT JSON with exactly these keys and nothing else:\n\n```\nbrief (one dense sentence on the concept), subject (pose/action generically, NEVER a specific\nidentity), elements, location, composition, background, split (boolean), split_count,\nperson_count (0-3), emotion (one of the 11 presets or 'other'), emotion_detail (one vivid\nsentence covering eyes, brows, mouth, head angle)\n```\n\nThe reference drives energy/composition/style — it is NEVER sent to the generation model itself and never enters `medias`; fill any scene field the user left empty with derive-from-reference instructions (\"mirror the reference's framing logic...\").\n\n## Pitfalls\n\n- 4:5 exists only on Nano Banana; NO Seedream tier has 4:5 (v5_pro: 1:1, 4:3, 16:9, 3:2, 3:4, 9:16, 2:3; some model tables also list 21:9 — verify via `models_get` when it matters; the load-bearing fact is: NO Seedream tier has 4:5) — tweaks on a 4:5 render go to 3:4 (disclose the downgrade).\n- `seedream_v5_pro` is gated to paid plans (BASIC+) and has already been pulled from the catalog once (2026-07) — if the submit errors or the model is absent, fall back to `seedream_v4_5` (quality 'high' ≈ v5 2k) instead of retrying.\n- Faces drift even with the Identity Lock (stochastic) — inspect every render against the face reference before showing it; the fix is a re-render, not an apology.\n- One submit per variant: batching via `count` breaks per-variant prompts (emotions/takes each need their own prompt).\n- Final readability test: a thumbnail competes at ~120px wide in a sidebar — the emotion and the hero element must still read at that size; if they don't, the composition (not the resolution) is wrong.\n"},"changes":[{"path":"/included_files","type":"changed","before":[{"relative_path":"references/text-overlay-bake.md","size_in_bytes":11385},{"relative_path":"references/thumbnail-frameworks.md","size_in_bytes":4063}],"after":[{"relative_path":"agents/openai.yaml","size_in_bytes":259},{"relative_path":"references/text-overlay-bake.md","size_in_bytes":11385},{"relative_path":"references/thumbnail-frameworks.md","size_in_bytes":4063}]}],"summary":"Fields changed: 1. /included_files.","summary_kind":"deterministic","summary_metadata":{}}