Pipeline Hub

dawn-statics

Non-native STATIC ad generation for DawnBands (and the template for other brands). Fish 2026-09-04, permanent - "keep brand aesthetic / subavatar core emotional appeals permanently in our non native static ad generation pipeline." Use for any static/image ad batch that is not a native (natives = native-image-gen). Enforces the PDP brand kit, one sub-avatar card item per static in her verbatim words, the qualifier ban, LED-unlit product lock, layout-only references, and a mechanical gate before any image is generated.

Lane: — · not built yet: 0 (see below) · source: /Users/ayden/.openclaw/workspace/skills/dawn-statics

SKILL.mdEvals (0)Learnings (0)
The eight locks (every static, no exceptions)The copy method (Fish 9-04: "the statics became a lot better after you did this to the copy. Add this too")WorkflowNot adopted from the Evolve intake brief (and why)QA regen mechanics (first full run, SA2 9-04: 18 statics, 4 rejected on pass 1, 3 on pass 2, all clean on pass 3)Review gate is mandatory (Fish 2026-10-04: "is this static check running for all static generations from now on")Next format lane: rip templates from NON-direct competitors (Fish 2026-10-04, binding for upcoming static batches)Reference implementationFish 2026-09-30 rulings (audit of hub 2026-09-30-dawn-band-native) — binding10-06 upgrades (davidaistar diff)

dawn-statics — brand-locked, card-locked statics

Why this exists (receipts, 9-03→9-04): 30 statics shipped with each layout's own palette (plum, teal, magenta, yellow boxes); Fish: "there should be some sort of theme congruence with the dawn product page… purple ones are just so off." Same batch carried my phrases in her slot ("the other one", "leftover mom", "different person for the first hour") and invented events; Fish: "fix all statics with real voc / real not inferred feelings/events." Both are now mechanical.

The eight locks (every static, no exceptions)

  1. Brand lock. knowledge/brands/dawnbands/brand-kit.md + brand-board.png (pulled from the live PDP: off-white #faf9f7 / sand #f3ece4 / deep navy #2d3452; slate navy text #2f4a63; gold #fdac2b single accent; green checks; Halant serif headlines, Montserrat body; gradient lowercase dawn wordmark). The board is ALWAYS the third input image and the prompt carries the BRAND LOCK block. Re-pull the theme if the PDP changes (curl the PDP, grep font-family and --color-*).
  2. Product lock. product-ref-new-band-2026-08-27.png is the first input image; LED face UNLIT; never a square-faced watch.
  3. Card lock (the core emotional appeal). Each static's callout is ONE item from the sub-avatar card (experience / emotion / behavior) in HER words. Every voc line is grep-checked against the raw corpus (scripts/statics/voc_check.py; 0 hits = your phrase, rewrite). Photo-led or mechanism statics declare card_item instead. Quotes on image are verbatim and carry NO source line: no "a mom on Reddit", no subreddit, no survey credit (Fish 10-03 + 10-06: it shows it's just a random person). Gate rule12 fails any attribution. Diverse callouts across the set: no two statics on the same item.
  4. Qualifier + my-phrase ban. No clock times (one mother's verbatim "by 8 a.m." excepted), counts/durations, ages/grades, bus/phone/car/drop-off, checklists, lunch boxes, hair, uniforms, sibling gender outside a real quote; never "the other one", "leftover", "what's left of you", "different person", "first hour", "school version"; no similes, em dashes, "builds", "signal", "channel", "reports to nobody". ADHD named on every image.
  5. Rationale + alternates (added 9-04 from the Evolve intake brief). Every static declares why (which card row it opens: experience / emotion / behavior / desire / angle, and why that line stops her scroll) and alts (2+ headline iterations on the SAME card item; they become the ad's titles). Alts run the same qualifier gate as the headline.
  6. Congruency. The image depicts the card item, not a mood. scene must share a content word with the headline / voc / card_item or the gate fails; photo-led layouts (tt-064 / 105 / 116 / 143 / 165) require a scene.
  7. Headline cap per layout, not global. Bold-headline layouts (tt-161, t01, t02, t05, t07) cap the headline and its alts at 8 words. Quote cards (tt-121, tt-045, tt-064) keep her full sentence; a cap there forces paraphrase, and paraphrase is my phrase in her slot.
  8. Layout-only references. knowledge/brands/dawnbands/static-layouts/ (10 TrendTrack proven runners + 13 Evolve winning-ads templates). A layout ref supplies layout, type hierarchy and spacing; never its colors. Pull fresh layout refs from TrendTrack (/ads/query, image ads, proven runners) when Fish asks for new references; never from our own winners alone.

The copy method (Fish 9-04: "the statics became a lot better after you did this to the copy. Add this too")

Run this on every line that will sit on a static, in this order, before the spec is written: 1. Grep every line against the raw corpus (scripts/statics/voc_check.py: raw thread dumps, the 774-quote VOC bank, 10-03 Reddit comment JSON, voc-pull-* pulls, the buyer survey CSVs, Fish's notes; never docs we wrote; prints the file of every hit; competitor + Okendo hits are listed as NOT COUNTED, 10-06; --files lists the corpus). Zero hits = our phrase. Replace it with the nearest line a mother actually wrote; do not paraphrase around it. Receipts: "the other one" → "the other kid" / "the little one"; "leftover mom" → "I feel so bad for the other kid"; "different person for the first hour" → "comes downstairs in a foul mood" / "fine at school, like it never happened"; "what's left of you" → "we're all stressed and running late every single day". 2. One card item per static, stated as she states it. No composite headlines that stack two ideas. Fifteen statics in a unit = fifteen different doors (experience / emotion / behavior rows of the sub-avatar card, plus the "Separate angles" table). Quotes stay verbatim and land on their own, with no source line (gate rule12). 3. No qualifier that is not the card's own item. Clock times (one mother's verbatim "by 8 a.m." is the only exception), counts and durations, ages and grades, bus / phone / car / drop-off, checklists, lunch boxes, hair, uniforms, a sibling's gender outside a real quote. Each one excludes a mother whose morning looks different for a reason unrelated to the desire. 4. Mechanism = the three ruled facts, plain. Wake-up spikes stress hormones before his feet hit the floor / he starts the day in fight or flight / an ADHD brain takes longer to come back down; then the product line (wakes on the wrist, no sound, no startle, turns it off himself). No "builds", "signal", "channel", poster fragments, inversions. 5. Captions match the post. Image copy is in the script's voice (second person "your ADHD kid…") and promises only what the primary text delivers ("why it happens is in the post", never "what changed it is below" when the body has no result story). Meta shows the primary text above the image: say "the post", not "below". 6. ADHD named on every image. Add it to the product line if the callout does not carry it. 7. Gate, not habit. gen_statics.py --check fails the spec on any zero-hit voc line, banned qualifier (headline, sub, bullets, attribution AND alts), missing card item, missing ADHD, missing layout ref, missing why / alts, over-cap headline on a bold layout, scene that does not depict the card item, the belief chain (rule9: default = 9-04 Apology-Mom chain; a spec or static may declare its own chain), before any image is generated. Opt-outs go in waivers ({"slug","rule","reason"}, rule id like rule9 or a failure substring; no reason = FAIL); --check/--list/--out/--dry-run and every 10-06 flag apply them and print each as WAIVED (10-06; spec format in the gen_statics docstring, items 10-11). 8. Approval before render. gen_statics.py --list prints the sheet (slug / layout / headline / item / why / alts / scene). Fish approves the callouts on that sheet; only then --out. A 30-static rebuild costs less at the sheet than at the hub.

Workflow

  1. Concept formula first (Fish 2026-10-03, binding): every static = proven structure (our long-running grid or a competitor long-runner) + verbatim customer line + belief they hold + buyer who converts. See knowledge/concept-formula.md.
  2. Read the sub-avatar card (research doc: card rows + the "Separate angles" table). List 10-15 callouts, one card item each, verbatim where a quote exists.
  3. Write spec.json (shape in scripts/statics/gen_statics.py docstring): slug, layout, scene, lines (headline / sub / bullets; never attribution, rule12), voc or card_item, why, alts (2+).
  4. python3 scripts/statics/gen_statics.py spec.json --check → must PASS.
  5. python3 scripts/statics/gen_statics.py spec.json --list → paste the sheet to Fish; he picks / cuts callouts. Fix the spec, re-run --check.
  6. python3 scripts/statics/gen_statics.py spec.json --out <batch>/statics (--out is required on every render path since 10-06, no ./statics default; GPT Image 2.5 on fal, 2048 square, product + layout + board + logo as inputs; --backend nb only if Fish asks for the old Nano Banana Pro path; layouts can come from knowledge/brands/dawnbands/static-templates/ (make_prompts.py); run uv from the workspace dir, not the output dir).
  7. Design review is automatic (10-02, scripts/statics/static_review.py, wired into --out): Gemini scores every render on text-exact / feed-legible / breathing-room / hierarchy / simplicity / scene-fit / brand / product. Two-vote FAIL (hard fail or any axis <6) moves to <out>/rejected/ and re-renders once with the reviewer's fix as a TARGETED FIX (--retry-fails N, 0 = review only); WARN (mean <7) is kept and must be listed for Fish beside the static; review-summary.json per batch. Standalone: static_review.py <dir> --spec spec.json. Still eyeball full size for faces and reference artifacts; the reviewer misses borderline garbled props about half the time.
  8. Statics carry the sub-avatar script (hook 1) as the one body + 3 titles + UTMs (launch_angles.py pattern; url_tags on every creative). Stage to the hub, Fish image approval gates launch.

Not adopted from the Evolve intake brief (and why)

The 11 famous-brand example ads (Ryanair, Tesla, Patrón, Corona, Chipotle, Huel…) as the headline source: nine are wordplay for brands with existing awareness; the said-out-loud and direct-but-new rules ban that register. Per-example palettes: the 9-03 purple problem. A global 6-8 word cap: kills her verbatim sentence on quote cards. Curiosity-only openers with no reader named: the parked unaware lane. Our layout refs + the card + the corpus replace all of it.

QA regen mechanics (first full run, SA2 9-04: 18 statics, 4 rejected on pass 1, 3 on pass 2, all clean on pass 3)

Review gate is mandatory (Fish 2026-10-04: "is this static check running for all static generations from now on")

Next format lane: rip templates from NON-direct competitors (Fish 2026-10-04, binding for upcoming static batches)

Fish: "need to start trying different static formats too in the future ripping template from non direct competitors like native BOF like the band in real life with a note for example." - Source formats from long-running static winners OUTSIDE the wrist-alarm category (adjacent mom-buyer brands: Saphire, Hello Bloom, First Day, EllaOla, Loftie, NATPAT, Big Life Journal; plus any DTC long-runner), found on TrendTrack (browser route, CDP 9333) by days running / duplicates, then live-checked on FB Ad Library. Our own D01–D14 grids are the control, not the source. - Priority format: native bottom-of-funnel "product in real life": a real photo of the Dawn band in an actual home moment (nightstand, kitchen counter, backpack strap, a teen's wrist) with a handwritten or sticky note / text-message overlay carrying the callout. It should look like a mom's photo, not an ad. - Rip = copy the FORMAT (layout, photo style, note device, text placement), never their copy or claims. The band must be the real product (photographed or real-cutout composite); the callout is our verbatim VOC; the review gate still applies (static_review + hub gate). - Each new format gets its own ad set (one variable = the format) against our proven body, with a floor.

Reference implementation

output/dawn-sa2-apology-2026-09-04/ — spec.json (18, eight-lock gate, why/alts/button/fix fields), script.json, build_ads.py, hub/ (staged as hub session 2026-09-04-dawn-band-static). Older: output/dawn-evolve-angles-2026-09-03/ — spec-a4.json / spec-a5.json (30 statics, five-lock gate), jobs-statics-6…9.json, gen_nb.py, sheets. Plan: ~/.claude/plans/dawn-evolve-angles-2026-09-03.md.

Fish 2026-09-30 rulings (audit of hub 2026-09-30-dawn-band-native) — binding

10-06 upgrades (davidaistar diff)

Source: ~/research/davidaistar/analysis/diff_statics.md (top-5 items 2-5). All in scripts/statics/gen_statics.py, fal backend only. The eight locks hold on every pass: input order is always 1 product, 2 layout ref (or the image being edited), 3 brand board, 4 logo, 5 scale ref (hero only); PROD + BRAND LOCK + HIER blocks ride every prompt; gate() (VOC grep, qualifier ban, card item, ADHD, why/alts) runs before any path renders. Spend convention unchanged: gate PASS, --list sheet approved by Fish, then an explicit --out. The new paid flags refuse to run without --out (or --dry-run). Backup: gen_statics.py.bak-2026-10-06.

Flag When What it does
--new-layout First use of a layout that is NOT a proven D0x template Unlocks --improvise / --candidates >1. Refuses any static whose template or layout ref is D\d\d: proven templates keep the one-pass exact-text render. Code-level "proven" = D\d\d ONLY: the tt-/t0 files in static-layouts/ (TrendTrack runners, Evolve templates) pass as new, so don't use --improvise on them unless the layout is genuinely new to us.
--improvise New layouts only (with --new-layout) Pass 1 <slug>.layout.png: layout + product + scene, placeholder copy, no exact text (forcing exact copy on gen 1 degrades composition). Pass 2 = edit pass on that image with the exact text, then the normal review + retry. A retry re-runs only the edit pass; delete <slug>.layout.png to redo the composition.
--candidates N New layout, N = 2-3 N compositions per slug, all saved (<slug>.cN.png, review sidecars written, nothing moved or picked). With --improvise: N layout-only bases (<slug>.layout.cN.png), no review, then finish the pick with --recycle.
--recycle PNG (+ --only SLUG) Refresh an approved static with a new card line Edit pass: the approved PNG goes in slot 2, only the words change. If --out already holds <slug>.png (e.g. the approved base itself), the edit writes <slug>.r2.png (then .r3...) and reviews that file; the base is never touched (fixed 10-06, dry-run verified). Spec must resolve to one static. Gate runs on the new text; refuses a base whose .review.json FAILED; warns if no sidecar. The output goes through static_review like any render. The PIL path ("Product fidelity, hard path" above) is still the deterministic option when the edit drifts.
--scale-ref PATH Product-hero statics: D01 / D10 / D13, or "product_hero": true Adds a hand-held product photo as the 5th input so the model sizes the band from a hand, not a flat studio shot. Default knowledge/brands/dawnbands/product-ref-handheld.png.
--edit-model ID Testing a text-pass model Model for the edit pass only (improvise pass 2, recycle). Default = GPT Image 2.5 edit (proven). The registry candidate ideogram/v4.5/edit ("Exact text on image" role) is UNTESTED: its fal request body may differ from GPT Image's, so dry-run it, then run one paid test before any batch uses it.
--dry-run Any of the above (fal only) Prints every prompt and input list. No API call, no review, no files. Honoured on --backend nb too (fixed 10-06: prints the nb prompt + inputs, no call). Any render without --out or --dry-run refuses (fixed 10-06).
--only a,b / --spec Slug filter; --spec is an alias for the positional spec.

Scale-ref asset (missing as of 10-06): product-ref-handheld.png does not exist yet. Until it does, every product-hero static prints a warning and renders without it. Per the 10-05 rule, it must be a REAL photo of the Dawn band on a wrist or in a hand: our own shot first. An Okendo review photo only after checking it against product-truth (diagonal ribbing, small flush display, dark when idle): 94.6 % of the Okendo corpus predates the store's first order (output/dawn-bottleneck-2026-10-03/E-shopify-nonbuyer.md §6a), so its photos may show another unit. Never generate it. Strip EXIF and crop it to band + hand. Note SCALE_RULE in the code still says "Okendo review photo or our own shot" without that check.

Hub staging: stage only the final <slug>.png. Intermediate *.layout*.png and unpicked *.cN.png stay out of the hub folder (no sidecar / not picked).