Per-phase gpt-image-2 title cards composited via Remotion, tested against 7 real, already-rendered ads — 4 distinct products (Gymshark, Pelagic Gear) spanning Reels, Stories, Feed and PMax landscape, plus one product's 3 real sibling ads across every funnel-stage intent profile. Flag ADGEN_DIRECTOR_TITLE_CARDS stays off pending the fixes below.
2 defects · not ready to ship
0:01.9hook
0:05.2proof
0:07.2closeAll 3 timestamps render the hook card. The proof/close cards were generated and billed but never appear — at 7.2s the close frame still reads “The top for your run and your coffee run” instead of the product name + benefits, and it now sits directly over the shark logo.
0:01.2hook
0:05.7closeSame sequencing defect as Reels — the close-phase timestamp still shows the hook headline. Confirms this isn’t a Reels-specific quirk; it reproduces on every 3-phase vertical format tested.
single cardmainSingle-phase format has nothing to sequence. Clean two-weight typesetting, correctly inset to the square safe zone, no collision with the logo mark. This is the case the feature already gets right.
0:00.8phase 1
0:08.3phase 2Cards do switch here — but phase 1 renders as a dithered, washed-out ghost instead of crisp text (an alpha-compositing artifact), and phase 2’s benefits copy overflows past the bottom of frame while still double-exposed against phase 1’s residue.
Copy varies today by exactly one production input: Ad.funnelStage. These three are
genuine sibling Ad documents for the SAME real product — Pelagic Gear’s
“Faroe” flannel, $80, Reels 9:16 — each independently rendered by production for a different
stage. Nothing about the request was forced; resolveStagePresetName(ad) read each
ad’s own stored funnelStage exactly as the live renderer would.
hookheadline
proofheadline
closename+benefitscanonical-awareness: closing card carries productName and the full
benefits list — “Lightweight all-day comfort · Versatile coastal style · Soft yet
durable build.” Reads as a discovery ad: explain what it is and why it's good.
hookheadline
proofheadline
closename+benefitscanonical-consideration: identical headline and closing content to awareness in
this ad — same productName + same benefits list. Whatever this stage is meant to change didn’t
surface as different TEXT here; if consideration should differ in substance, not just downstream targeting,
that's a real gap to design for.
hookheadline
proofheadline
closename onlycanonical-conversion: the ONE real content difference across all three stages
— the closing card drops the entire benefits list, showing productName alone with no price, no CTA,
no urgency. If conversion-stage ads are meant to close harder, this preset currently does the opposite:
it says less. Also reproduces the sequencing bug — every sampled timestamp on this ad shows the
hook headline, so in practice a real conversion ad never even reaches this thin closing card today.
On both 3-phase vertical ads (Reels, Stories), proof/close cards are generated and billed via a real gpt-image-2 call, then never displayed — the composite holds the hook card’s image for the full render regardless of the sampled timestamp. Paid content silently never reaches the viewer on the two most common ad formats. The 2-phase landscape case does advance, so the defect is specific to how 3+ sequential phases wire into the composite, not the fade/timing envelope itself. Confirmed a third time on a completely different real product (Pelagic Gear “Faroe”) across all three funnel-stage siblings — not a Gymshark-specific or single-ad fluke.
repro: ad 6a9c65e6…50c8 & 6a9c65e8…50f5, timestamps 5.2s / 7.2s & 5.7s · also 6a9b6f1a/1b/1c…92bae/bc7/bcc (Faroe, all 3 funnel stages)First card composites as a dithered, checkerboard-alpha ghost instead of opaque text. Second card’s benefits copy runs past the bottom edge of frame and shows a visible double-exposure against the first card’s fade-out.
repro: ad 6a9c65e5…50b2, frames at 0.8s & 8.3sA tight crop on the Reels close-phase frame shows generated title copy sitting directly over the stitched Gymshark logo. The card is generated blind to the actual video frame, so this class of collision is a structural risk of the approach rather than a one-off render.
repro: ad 6a9c65e6…50c8, frame at 7.2sAcross the same real product's 3 funnel-stage siblings, awareness and consideration both close on productName + the full benefits list. Conversion — the stage where the ask should be hardest — closes on productName alone: no benefits, no price, no CTA text in the card. Whether this is intentional design (CTA/price live in platform chrome, not the card, at conversion) or an oversight is a product question, not a code bug, but it's worth a deliberate decision either way.
repro: ad 6a9b6f1a/1b/1c…92bae/bc7/bcc (Faroe), close-phase cardReels/Stories safe zones were split from one collapsed vertical entry, the
keep-out band gap is closed, and safeZoneKey now flows through the CJS plate-scan
path in production (adgen #59 / backend #335). The one loose end is a stale line in
session.d/KNOWN-OPEN.md still describing it as open — code disagrees with
the doc, not the other way around.
| Ad | Format | Cards | Spend | Result |
|---|---|---|---|---|
| 6a9c65e6…50c8 | Reels 9:16 | 3 | $0.1806 | sequencing bug |
| 6a9c65e8…50f5 | Stories 9:16 | 3 | $0.1304 | sequencing bug |
| 6a9c65ea…5113 | Feed 1:1 | 1 | $0.0100 | pass |
| 6a9c65e5…50b2 | PMax 16:9 | 2 | $0.0702 | composite artifact |
| 6a9b6f1a…92bae | Reels · awareness | 3 | $0.1304 | sequencing bug |
| 6a9b6f1b…92bc7 | Reels · consideration | 3 | $0.1304 | sequencing bug |
| 6a9b6f1c…92bcc | Reels · conversion | 3 | $0.1304 | sequencing bug + gap |
| Total | $0.7824 | |||
scripts/renderDirectorTitleCardsPreview.js) drives
the real directorTitleCardService + directorTitleCardGenerate
+ Remotion TitleCardSlot stack against each ad’s existing untitled master
(veoVideoUrl), read-only against the production database — Ad-collection writes
are blocked at both the driver and model level, and adId is omitted from every
generate call so no spend receipt lands on the ad. Videos and stills here are re-encoded for file size; visual
defects reproduce at source resolution.