Ad Campaign — plan → generate → judge → package
A repeatable pipeline for shipping a set of on-brand image ads at volume. Built from a real 30-ad campaign that went 75/75 accepted at ~7.5 credits per shipped asset, then re-validated cold on a smaller executor model with zero improvisation.
Core loop: copy is judged as text before it costs credits as pixels; every image is judged with eyes before it's called done.
Design note — no improvisation required
The creative judgment is front-loaded into frozen assets. Execute them, don't re-derive them:
- Fixed prompt blocks are FROZEN STRINGS (
references/prompt-template.md
). Fill the brand slots once, then only the four per-ad slots ({SCENE}/{hook}/{key}/{support}) change. Never paraphrase, "improve" or re-order the fixed clauses — identical wording across the set is what makes 30 ads read as one campaign.
- Scenes are built by formula, then remixed from your accepted-scene library (see "Grow the library" below) — not invented from zero each time.
- Judging is literal rubric application (
references/judge-rubric.md
), not vibes. Uncertain between FLAG and FAIL → FLAG and log. The ignore-list is binding.
- Decision points are enumerated in Step 0. Anything outside them: reversible → make the obvious call and log it; spends meaningfully beyond the agreed budget or changes brand/copy rules → stop and ask the user.
When to activate
- "build an ad campaign / a set of ads / paid-social creative"
- A batch of on-brand image ads at volume (10+), with text on-image
- Re-running the system for a new brand (swap the brand kit, keep the pipeline)
Step 0 — Lock the variables (intake)
Ask the user; if they're unavailable, lock the recommended defaults, state them as veto-able, and keep moving.
| Variable | Recommended default |
|---|
| Angles | The brand's active sales angles/offers, from wherever the brand keeps them. A NEW angle gets written into the brand doc FIRST, then used — brand context outlives the campaign. |
| Volume | 30 masters = 1/day for a month. Direction gate of 2–3 before the rest. |
| Placements | Meta feed + story. gpt_image_2 has NO 4:5 → generate 3:4 composed to survive a TOP-trim crop (headline upper-third, CTA/footer at the bottom edge). |
| Story twins | Default OFF. If feed posts get reposted to stories anyway, 9:16 twins double the credit spend for the same creative. If a one-off vertical is explicitly requested, re-stage it (see template), never resize. |
| Sequences | One 3-frame 9:16 arc per angle (distinct creative, not a twin): hook (no button) → turn (no button) → CTA (prominent pill). Button only on f3 is what makes it an arc. |
| CTA register | ADS ARE ASSERTIVE: solid accent-colored {CTA_TEXT} pill + cost-of-inaction urgency. Soft asks ("worth a quick chat?") belong in email — never on paid placements. |
| Audience | BROAD + diverse visual worlds, enforced by quota (Step 2). Never let one niche dominate. |
| Footer/domain | {FOOTER_TEXT} tiny mono at the extreme bottom edge. |
| Captions | Full pack per ad: hook as first line, founder-voice body, CTA → domain, 4–6 real hashtags. |
| Budget | 7 credits/image at quality high + 2k. Check higgsfield account status
before and after. |
Step 1 — Fill the brand kit (once per brand)
These slot into the frozen template and then get treated as frozen themselves:
| Slot | What it is | Example |
|---|
| Deep near-black base grade | |
| Off-white text color | |
| The ONE accent color | |
| Ad CTA label, lowercase | |
| Brand — domain footer line | |
- Read the brand's positioning/ICP/voice docs if they exist; the pipeline is brand-agnostic, the taste bar isn't.
- Exactly ONE accent color — restraint is what makes a set read premium.
- The template's type classes (heavy lowercase grotesque headline + monospace support) are part of the proven recipe. Swap them for the brand's own type only if you must — and re-run the direction gate if you do.
Step 2 — Campaign plan + copy matrix (before any prompt)
Write
+
. Every ad exists as a row first:
id · angle · hook · key · support · world.
Copy rules (voice-checked as text, cheap):
- Hook ≤9 words, lowercase, blunt. Support ≤12 words. No exclamation marks, no AI-tell words, no fake urgency (cost-of-inaction urgency OK).
- Exactly ONE keyword per hook in the accent color.
- Never name the niche out loud — the visual world codes who-it's-for (a docket rail says "restaurant" without saying it). World-cue NOUNS inside the pain are fine ("service ends at ten"); niche LABELS are not ("for restaurant owners").
- No client names. Sell WHAT it does / the outcome, never HOW / the internal mechanics. No domain in headlines.
- World-spread quota, written down before generating: across a 30-ad set, no world more than ~3×, cap your most-tempting world, include several universal metaphors and at most 2 type-only ads. For a sub-batch apply the spirit: every world distinct, no repeats of recent campaigns' worlds.
- Mix day and night scenes across the set; grime is banned — premium-worn yes (patina, aged brass, worn oak), filthy never (dirt/mud/stains/crusted = judge FAIL). A dark grade holds in daylight too.
Step 3 — Build prompts from the template
One
per ad:
(3:4 masters),
prompts/seq/{angle}seq-f{1,2,3}.txt
(9:16 sequences). The full template with clause-by-clause reasoning:
references/prompt-template.md
. Non-negotiable clauses: all on-image text quoted + "exactly as quoted"; any incidental surface text forced to "illegible blur"; ends with "No other readable text anywhere, no watermark, no logo, no border."
Step 4 — Direction gate (before spending the batch)
Render 2–3 covering the distinct visual modes (one human scene, one macro metaphor, one type-only). Judge them (Step 6 rubric). Only a pass on all modes unlocks the full batch. If the user is present, show them the gate renders.
Step 5 — Generate
bash
higgsfield generate create gpt_image_2 \
--prompt "$(cat prompts/final/a1.txt)" \
--aspect_ratio 3:4 --quality high --resolution 2k \
--wait --json > generated/a1-r1.json
- Full-wave runs: batch sequentially inside a background task. Tight-cap runs (dry runs, gates, ≤5 gens): serialize in the foreground and inspect each JSON before firing the next call — a failed generation still counts against a hard cap.
- Extract from each JSON → curl to
generated/{id}-{ratio}-r{round}.png
. Inspect the JSON before curling — a 502 writes HTML into the file, and an empty result_url means the render failed server-side.
- Auth dies occasionally: (device flow) — surface the URL to the user (copy it to their clipboard if you can). Balance:
higgsfield account status
.
Step 6 — Judge loop (eyes on every image)
Read every PNG with vision. Score 7 criteria:
text accuracy · instant read · who-is-this-for · no slop · brand world · slap factor · craft. Verdict per image with reasons →
. Full rubric, thresholds, and the known-nits ignore list:
references/judge-rubric.md
.
- Accept = zero FAIL and ≤1 FLAG. Rejects regen ≤2 rounds, then the concept is cut, not forced.
- Credit discipline: set-level nits invisible at delivery size (apostrophe glyphs, pill drift, footer under platform chrome) are logged, never regenned.
- Known failure modes: 502 → re-run same command; content filter (empty result_url twice) → de-brand the object noun ("whiskey cask" → "oak barrel aging cellar").
Step 7 — File + package
- Accepted → (flat archive), then per-angle folders: masters + + that angle's + top-level (cadence, ad-button choice, crop rules, honest flags, provenance).
- Captions: hook as line 1 (captions truncate), founder-voice body, , 4–6 hashtags. Meta button: Contact Us / Get Quote.
Step 8 — Honest report
Accepted/rejected counts with reasons, every flag surfaced (never buried), credit spend before/after, paths. Give paths — don't auto-open files.
Running as a SUBAGENT: harness guardrails may block writing a REPORT.md file — return the full report as your final text instead. The on-disk artifacts (plan/copy-matrix.md, judgement-log.md, prompts/, generated/) write fine and are the audit trail.
Grow the library
Keep every accepted ad's
sentence and its verdict from
. That corpus is the next campaign's scene library — remix proven scenes instead of inventing from zero, and the judge log becomes training data for what your brand accepts.
References
| File | Contents |
|---|
references/prompt-template.md
| Master template + sequence rules, clause-by-clause WHY |
references/judge-rubric.md
| 7 criteria, accept thresholds, regen policy, ignore-list, filter/502 fixes |