← Both skills · wildhorn-static-ads · wildhorn/PLAYBOOK.md
This playbook turns any winning static ad template (any brand, any niche) into a Wildhorn ad that Adrien validates on the first try. It is the Wildhorn layer of this skill, on top of its generic folders:
2-make/ (start with 2-make/PIPELINE.md) and 3-improve/ — the brand-agnostic machine (decode → inventory → spec → lint → engine chain → composite → QC → review → rules). Read them for the generic method and tooling depth.1-understand/ (1-understand/ad-dna.md, 1-understand/avatar-angle-system.md, 1-understand/competitor-case-studies.md) — how to read and decode an ad (product → mechanism → angle, avatar, scroll-stop trick). Read it when a template is hard to decode; don't duplicate it here.This skill alone is enough to run a Wildhorn batch. The wildhorn/ folder holds the Wildhorn depth:
| Reference | Read it when |
|---|---|
wildhorn/brand.md |
writing any copy or prompt: product facts, label, offer, proof, avatar, banned words, assets, fonts/colours |
wildhorn/angles.md |
choosing the angle and headline: every angle, mechanism, every validated headline, and the lines that failed |
wildhorn/lessons-wildhorn.md |
before every spec and every QC: the full rulebook with evidence (A1–A33, B1–B20, C1–C18, D1–D13, E1–E13) |
wildhorn/adrien-feedback.md |
before a review, and when ingesting a Loom: chronological digest of all 12 Looms, the 261-row coverage matrix, how Adrien reviews |
wildhorn/winners-catalogue.md |
before cloning: find a validated ad with the same template brand/format and reuse its pattern (83 Sam + 79 run 1) |
wildhorn/failures-catalogue.md |
before sending a spec: every rejected try grouped by failure type + a 12-point pre-flight checklist |
wildhorn/paths-and-commands.md |
to run anything: VPS paths, scripts, pages, exports, checkpoint, copy-paste commands |
Track record. Run 1 (79 templates, research-first method): 0 % first try, 261 generations, 9 review rounds. Sam batch (83 templates, decode + pixel inventory + lint): 71 % first try (59/83), 100 % final (83/83), 133 calls. 162 validated ads live at https://blueprint.machineroom.app/all-ads. The difference is the method below — follow it to the letter.
ssh vps-claude, workshop /root/workspace/wh-remix/). Mac = only ~/Downloads exports and temp files for viewing images. Never run Chromium/Playwright on the Mac.demand → status table.~/.claude/bin/checkpoint wildhorn-batch-test. Write it at the end of each round.~/.claude/bin/checkpoint wildhorn-batch-test — where we left off.wildhorn/lessons-wildhorn.md (all rules) and skim wildhorn/failures-catalogue.md §checklist.wildhorn/winners-catalogue.md for the same template brand/format.lint.py + D13 template check → fix until clean.srun.py; QC every image at full res; fix before showing.validated.json, sell_override.json) → redos + next 10.~/Downloads/Wildhorn-<Batch>/, stats, kit/creatives.jsonl, checkpoint, recap table.wildhorn/brand.md)/root/workspace/wh-remix/product-jar.jpg (Image 1 of every generation); jar cutout /srv/wildhorn-lp/p6/jar-30.webp (composited, never regenerated).Decode
Copy
Visual
Production
lint.py, blocking) and the template path must match the S-number (D13).composite.py with the real jar and exact text; Nano Banana Pro for text painted into the scene. Never water an ad down to pass a filter (D1, D5, D7). Fallback must keep every placement (D6).jarswap.py + script text, keep aspect ratio) before re-prompting (A25).Cadence: 10 ads per round (D12). Each round = redo every rejected ad + the next 10 templates. Adrien reviews each round with a Loom.
~/Downloads, iCloud BUSINESS/WILDHORN CONCEPTS/, or images in chat — if pasted in chat you don't have the file: ask for the file or describe the layout carefully)./root/workspace/wh-remix/batch3/ with templates/ prompts/ logs/ sell/ validated/ (or keep extending sam-batch/ with s5specs.py, s6specs.py… if he says "continue the Sam batch").scp -r "<folder>"/* vps-claude:/root/workspace/wh-remix/batch3/templates/. Keep original names. Numbering = position in the sorted folder — state it on the review page.kit/creatives.jsonl and wildhorn/winners-catalogue.md: same template already done? Same brand/format? Reuse what won (S70, S75, S77, S78 were first-try reuses of run-1 winners).For each template, before writing any copy:
template_has_deadline=True only if it has one.openai with fallback="seedream_comp" by default; sexual/skin → sexual=True + seedream_comp with sd_prompt + composite; text-only → pure_comp; painted text → nano via kie_gen.py.Spec format (Python dict in sNspecs.py, list S):
OLD = "a man in his late 40s with short greying hair and light grey stubble"
CAPS = "transparent capsules packed with small recognizable deep-red organ silhouettes (mini hearts, mini kidneys, mini livers), clean, no visible veins"
HORN = " Any brand mark outside the jar label is the horn icon ONLY (two curved horns), NO letters."
S = [
dict(num=84, tpl="10-03_c0201_Brand.png", angle="A1 Beer belly (same angle — copy)",
why="<the scroll-stop trick in one sentence>",
dna="<their product, symbols, body part, logo, offer, formula> → <our swaps>",
scene="<why the scene is here> — applies / moved to <where the beer belly shows>",
placement="<exactly where the jar is, how it is held, how many jars>",
inventory=["KEEP …", "KEEP …", "SWAP their pouch → WILDHORN jar …", "SWAP their model → " + OLD, "KEEP headline block top-left, same spacing"],
visual="<full visual description written from the inventory>" + HORN,
texts=["Headline: \"BEER BELLY?\"", "Subline: \"…\""],
engine="openai", fallback="seedream_comp",
# sexual=True, sd_prompt="<scene only, no text, no product, empty zones for text>",
# composite={"jar": {"x": 0.8, "y": 0.95, "h": 0.32}, "texts": [{"y": 0.06, "size": 0.08, "font": "Anton", "runs": [["BEER BELLY?", "#FFFFFF"]]}]},
# template_has_deadline=True, tpl_abs="/root/workspace/wh-remix/out/s_84_v1.png",
),
]
srun.py builds the final prompt: product LOCK (Image 1 = jar, exact label) + "IMAGE 2 = the template ad to clone 1:1" + PIXEL INVENTORY + TEMPLATE DNA + SCENE LOGIC + PRODUCT PLACEMENT + CHANGES + TEXT (exactly, nothing else) + constraints (1:1 square, re-flow vertical templates without dropping elements, render only the listed text once, nothing of their product world, adults over 25, jar = exact replica).
ssh vps-claude 'cd /root/workspace/wh-remix && python3 lint.py sam-batch/s5specs.py'
Then the D13 template-path check (wildhorn/paths-and-commands.md §6). Fix every error; never bypass. Lint blocks: missing decode fields, banned words, "belly" without "beer", invented deadlines, generic/raw/veiny/brown organs, missing inventory/placement, sexual on OpenAI without fallback, fallback without composite, jar-count parity, seedream_full, bare WILDHORN wordmark line, emoji in composite.
ssh vps-claude 'cd /root/workspace/wh-remix/sam-batch && printf "%s\n" 84 85 86 87 88 89 90 91 92 93 > jobs5.txt && nohup sh -c "cat jobs5.txt | xargs -P 5 -I{} sh -c \"python3 srun.py s5specs.py {} > logs/r5_{}.log 2>&1\"; touch logs/_r5done" >/dev/null 2>&1 &'
~110 s per gpt-image-2 image (quality high, 1024²). Refusals switch automatically to the fallback. Every call lands in calls.jsonl.
Copy the new out/s_NN_vK.png to a local temp folder and Read each one at full resolution, template next to it. Run the 12-point checklist at the end of wildhorn/failures-catalogue.md plus:
sam-batch/build_sam.py for a new batch): only ads to review, each row = template + every try (latest highlighted) + why / DNA / scene / text, validated ads hidden (D3). Add the hit-rate table vs previous runs.feedback/loom-<batch><round>.mp4; transcribe with mr-transcribe … --lang en (API only).wildhorn/adrien-feedback.md §3 — every distinct remark mapped to a rule; if none fits, write a new rule first (next free ID, origin, evidence) in LESSONS.md + wildhorn/lessons-wildhorn.md, and add a lint check when machine-checkable (D4, D11).validated.json for every ad he mentions (✅ first try / ✅ redo / ❌ reason). "Try 1 was better" → sell_override.json and freeze it (A20/A26). An ad he doesn't mention stays ⏳ — ask, don't assume (S62 waited a round).sam-batch/proto_sam2.py). Rebuild /lessons.tpl_abs when only wording changes. If an ad failed twice, stop re-prompting and apply A25 (how was THEIR ad made?)./sam-final-style page (every try + SELL VERSION + stats), /wildhorn-ads-style page (validated, images only), /all-ads (build_all.py, add the new set).rsync -a vps-claude:/root/workspace/wh-remix/<batch>/sell/ ~/Downloads/Wildhorn-<Batch>/ (new folder per batch).kit/creatives.jsonl (template · angle · headline · image) so nothing is made twice.~/.claude/bin/checkpoint wildhorn-batch-test --write) and update the Mac memory note wildhorn-remix-system.md with the load-bearing changes.demand → status table (✅ done+verified / 🔨 in progress / ⏳ blocked + reason).Template has text only, no scene? → engine="pure_comp" (solid bg + composite text). S4.
Template is a normal photo/illustration? → engine="openai" (gpt-image-2 high, Image1=jar, Image2=template), fallback="seedream_comp".
Sexual / lingerie / skin / bare chest? → sexual=True; OpenAI may refuse → seedream_comp:
sd_prompt = scene only, NO text, NO product, empty zones where text/jar go;
composite = real jar(s) at every placement + exact text (Montserrat/Anton/Oswald).
OpenAI is fine for suggestive FOOD objects (banana, glazed donut).
Man boobs close-up? → tight t-shirt / thermal / X-ray via OpenAI, or Seedream scene + composite.
Text must be painted INTO the scene → kie_gen.py --model nano (Nano Banana Pro spells well), then composite the jar.
(sunscreen, chalk, marker, clay letters)?
Their ad is a real photo with a pasted → A25: Seedream minimal edit of THEIR image (age the man, clear text, keep aspect)
product (jar as a head, etc.)? → jarswap.py on the jar box → composite text.
Only the wording must change on a liked try? → tpl_abs = that try; texts changed only.
NEVER → seedream_full (Seedream rendering text or the jar), emoji in composite, a WILDHORN letter mark.
Composite spec cheat-sheet: coordinates are fractions (x,y,h of the image). jar = one jar (y = bottom), jars = several (D6 parity with placement). texts[].runs = list of [text, colour(, weight)] for multi-colour lines; maxw wraps; shadow for text on photos; sunscreen for the lotion effect; strikes for crossed prices; rects/band paint solid zones first.
wildhorn/adrien-feedback.md.Recap format after each round (his trust mechanism):
| Demand | Status |
|---|---|
| S84–S93 generated + QC full res | ✅ 10/10, QC fixes: S87 text spacing |
| Loom 5 redos S42 S50 | ✅ regenerated, frozen tries untouched |
| New rules from Loom 5 | ✅ A32 added + lint check |
| Review link | ✅ https://blueprint.machineroom.app/sam |
jarswap.py, text by script, 9:16 kept → validated after 3 failures.| Template format | Wildhorn treatment | Proven by |
|---|---|---|
| Row/progression of objects (growing bananas, shrinking figures) | flip or keep the progression so it shows FEWER inches at the waist; figures face right toward the result | S1, S8, S16, S38, S44 |
| Same person split / two portraits (before vs after, "after X vs after X with brand") | frame waist-up so the BEER BELLY is the before/after; one problem state vs one solved state | S2, S52, S76, S83, #6 |
| One body cut in half (bloated half / toned half) | one single man, same skin and shorts, navel exactly on the cut | #77 |
| Month / week / day timeline photos | male side profiles or belly photos shrinking, facing right; keep their timeline only if they have one | S38, S40, S53, S69, #13, #16 |
| Thermal / X-ray / anatomical cutaway | the beer belly (or chest) glows; labels = his symptoms; no product if the template has none | S6, S22, S79, #2, #23 |
| Symptom checklist / "8 signs of…" | "8 SIGNS OF HIGH ESTROGEN IN MEN" with symptoms he recognizes | S5, S46, S49 |
| "X is not the problem, Y is" reframe | "THE BEER BELLY IS NOT THE PROBLEM. THE ESTROGEN DRIVING IT IS." — same sentence, two nouns | #21, S70, S52 |
| Us vs them table / comparison cards / check-cross lists | rival = fat burners / generic farmed organ pills; lines = results and pains, never features | #1, #3, S54, S65, S37 |
| Cartoon "I use X / I use Y" | X = the rival he knows (fat burners) | S42 |
| Meme (wojak/chad, Barbie/Ken, cat, Muppet, "said no wife ever", starter pack) | keep the meme mechanics; every line a scene from HIS beer-belly life; Ken = our 45+ man | #9, S47, #64, S81, S34, S61 |
| Clay / doll / Pixar / painted illustration | same render style, our man; never photoreal | S49, S52, S76, S83, S11, S15 |
| Visual pun on a shape | find an object with the same shape as the beer belly (moon, pear) | S23, S50 |
| Shock image (worms, gross pile) | equally shocking for the belly: melting like wax, beer cans + pizza crushing a scale | S7, S27 |
| Product ritual (powder in coffee, scoop) | capsules beside, the drink he already has (beer) poured as beer | S18 |
| Ingredient / "it's all in here" / puzzle product | 4 organs as recognizable silhouettes, one plain job each | S3, #32 |
| Product buried in its ingredients / billboard made of the product | fresh appetizing organs with kraft tags; billboard made of real organs | S67, #35 B7, #39 B6 |
| Hand holding the product, moody light, stat | jar in a man's hand; "93% saw their beer belly shrink*" + footnote | S24, S63 |
| Offer cards (special deal, last call, goodbye lowest price, bundle chooser) | $49.95 / ~~$100~~ / BOGO / 60-day guarantee; no invented gifts; never Autoship | S13, S36, S44, S74, #46, #49, #51 |
| Challenge / guarantee ("90-day challenge", "XL to M or money back") | keep their structure, our guarantee | S41, S68 |
| Text-only post / manifesto / handwritten | same words, only the excuse or the noun swapped; render by script or keep the handwriting | S4, S9 |
| UGC (TikTok captions, Day 0/Day 90 selfies, unboxing) | his voice, beer belly + her reaction; 50,000+ proof | S20, S21, S72 |
| Advertorial teaser ("Why men … can't …", "Read the solution →") | "Why men with a beer belly after 40 can't keep up with their kids" | S32, S33 |
| Celebrity/athlete holding the product | replace with our avatar, proof by his age ("AGE 51") | S31, S35 |
| Sexual innuendo object (pineapple, eggplant, cake log, donut) | decode it; keep it if it fits a men's product (eggplant, cake log), re-derive it if it belongs to their product (pineapple → banana / donut) | #57, #61, #69, #73, #74 |
| Sexual scene (couple, sheets, kitchen counter, her back) | older greying man; her desire tied to his lost beer belly; Seedream scene + composite when refused | S11, S12, S30, S55, S58, #65, #79 |
| Signed customer review | keep the signature with a woman's first name + initial | #79 |
| Mascot / personified problem | the beer belly personified (beer-mug character with a pot belly) | #66, #78 |
| Retro UI (arcade stats, emergency broadcast TV) | keep the whole UI; add one belly row / "viewers may see their beer belly shrink" | S28, S82 |
srun.py (verbatim, for manual prompts too)LOCK (top of every OpenAI prompt):
IMAGE 1 = product reference: the WILDHORN Wild American Bison capsule jar. Reproduce it exactly as in Image 1: amber glass jar, matte black lid, cream label with the WILDHORN wordmark and horn logo, WILD AMERICAN BISON, the bison illustration, Untamed Bison Nutrition, black band NATURAL TESTOSTERONE SUPPORT* / 30 CAPSULES. Real proportions, real 3D object with contact shadow and the scene's light. IMAGE 2 = the template ad to clone 1:1: same layout, composition, render style, palette, typography treatment, spacing and the SAME AMOUNT OF TEXT.
Body: PIXEL INVENTORY (KEEP / SWAP — follow exactly) · TEMPLATE DNA · SCENE LOGIC · PRODUCT PLACEMENT (copy the template) · CHANGES (visual) · TEXT (exactly, nothing else).
CONSTRAINTS (bottom):
USE CASE + CONSTRAINTS: Square 1:1 Meta feed ad, agency-grade. If the template is vertical, re-flow into the square without dropping elements and keep the template's spacing between text blocks. Render ONLY the listed text, spelled exactly, once — nothing added. Nothing from the template's product world may remain (product, brand, logo, flavor, packaging). Anatomically correct, adults over 25. CRITICAL: the WILDHORN jar, when present, is an exact replica of Image 1.
Add to visual when relevant: the HORN suffix (D8); "do NOT copy the template's person — " + OLD (A11); "NO jar, NO product" when the template has none (C7); the render style by name ("Pixar-style 3D, NOT photoreal"); the animal ("a wild American bison, NOT a cow, no spots") (C16).
Seedream sd_prompt: scene only — "No text, no product, no logo", name the empty zones ("the top 30 % is plain orange") so the composite has room (D7).
PRODUCT.md/kit/product.md list "Autoship -50%" in the locked offer and "Cible: hommes 40+" — both are site/targeting facts, banned in ad copy (B7, B2).s1specs.py (ORGANS "freeze-dried matte deep red-brown") and batch-08/09 would fail today's lint — don't copy them.Wordmark: "WILDHORN" text line (S19, S35, S43, S63, S71, S72, S73) from before D8; QC replaced misspelled marks. For new work: horn icon only.stats_run1.json ignores batch-09 (no meta.json) — its final map says B8 for ads whose final is B9 (except #74, really B8).jarswap.py's prompt is hard-coded for "jar replaces her head" — edit before reuse.