# RAW Chemistry — Meta ads: complete engagement record
**Compiled 2026-08-30 UTC / 2026-08-29 evening PDT.**
Written to be picked up cold by someone with no context. Everything is here: identifiers, every
ad day to the hour, every object and its state, every decision and its reasoning, every fix, every
mistake, every tool, every open question.

**Companion docs (all in repo):**
`ADS-PLAN.md` · `LEARNINGS.md` · `sources/competitive-research/competitor-audit-2026-08-28/` ·
`output/ufo-banner-2026-08-27/RECUT-NOTES.md` · `docs/META-ADS-MCP.md`

---

# PART 0 — TL;DR for someone picking this up right now

- **One campaign is live.** V81, CBO, currently **$175/day** (raised from $100 at 02:20 PDT Aug-29
  by an out-of-session MCP call — **operator has not confirmed this was intentional**).
- **Two ads running**, both the same 30.1s UFO video. One shows the offer only on the end card
  (control), one pins it on screen from 0.0s (banner). This is a one-variable test.
- **Three days of data.** Thu $6.11/ATC → Fri $6.13/ATC → **Sat $11.60/ATC**. Saturday broke the
  pattern, but **two things changed at once**: the budget *and* it being the first weekend day.
  They cannot be separated from history.
- **Recommended immediate action, pending operator go: revert budget to $100.** Saves ~$125.
  Do not try to read Sunday. Monday vs Thu/Fri is the clean comparison.
- **Campaign to date: $387.45 spend → $536.80 revenue, ROAS 1.39, 7 purchases, CAC $55.35.**
- **Blocked on four operator inputs**: COGS, a true reason-why for the 70% offer, confirmation of
  the budget change, and the go-forward budget.

---

# PART 1 — Access, identifiers, credentials

## 1.1 Meta

| item | value |
|---|---|
| Ad account | `928489503435693` — "Brett Ads" |
| Page | `107954045643337` — Raw Chemistry |
| Instagram user | `17841477912140752` — @rawchemistryus |
| Pixel | `622400256897443` |
| Access route | `mcp__claude_ai_Meta__*` MCP tools |
| Timezone | **America/Los_Angeles (PDT)** — all "today" figures follow this, not UTC |

**`instagram_user_id` is mandatory on video ads** under an all-placements ad set. Without it Meta
rejects with **error 1772103**. Statics do not need it.

## 1.2 Shopify

| item | value |
|---|---|
| Store | `raw-chemistry-store.myshopify.com` |
| Currency | **USD** |
| Markets | United States (primary, `localCurrencies:false`) · International (`localCurrencies:true`) |
| Admin token | **permanent offline** `shpat_` from own custom app |
| Custom app | "Webbuildr 2", client `ba66dfaf16713fd861fd4b70000ea92f` |
| Credentials dir | `~/.config/admaker/credentials/` (mode 0600) |
| Health check | `node tools/shopify-token-health.mjs` → expect `life OFFLINE` |

**CAD warning.** The operator browses from Canada and is served **converted prices** by the
International market. A discount shown as **−$334.28** is the **−$236.85 USD** discount
(rate ≈ 1.4114). Convert before diagnosing any pricing "discrepancy". Our own checks run from a
US-routed host and always return USD — which is what the campaign actually transacts, since the ad
set is geo-`US` only.

## 1.3 Other

| item | value |
|---|---|
| Competitor research | Foreplay MCP (`mcp__claude_ai_Foreplay__*`) |
| Review host | `http://15.204.83.60/` — static, read-only, mounts in `/srv/www/_server/mounts.json` |
| Repo | `/home/ubuntu/Documents/AdMaker` |
| Ledger | `node tools/history.mjs record` / `validate` |

---

# PART 2 — Complete object inventory

## 2.1 Campaigns (13 total, 1 active)

| campaign | id | status |
|---|---|---|
| **RAW \| V81 Clean Test \| UFO + LOOKUP + CUPID-R2 \| save70 \| Dark Posts** | `120249646488770611` | **ACTIVE** |
| RAW \| V82 UFO Persistent-Offer \| save70 \| Dark Post | `120249669199670611` | PAUSED, never spent |
| RAW \| V80 Single Test \| 3 Creatives \| 70% Arm | `120249645525260611` | PAUSED |
| RAW \| V79 Video CBO \| 8 Creatives | `120249625544390611` | PAUSED |
| RAW \| V79 Video ABO \| 4 Concepts x 2 Offers | `120249625270290611` | PAUSED |
| RAW \| V77 ABO \| 6 Ad Sets x $10 | `120249624812710611` | PAUSED |
| RAW \| V77 Offer A/B \| 3 Concepts x 2 Offers | `120249615068040611` | PAUSED |
| RAW \| V76 DCO Test \| 10 Concepts | `120249609686230611` | PAUSED |
| RAW \| V76 Video \| 4 Creatives | `120249608591340611` | PAUSED |
| RAW \| V76 Launch \| 6 Creatives | `120249599273780611` | PAUSED |
| RAW \| Test | `120249424103750611` | PAUSED |
| RAW \| Scale | `120249423872160611` | PAUSED |
| RAW \| Prospecting \| $100 Focused Test | `120249228401770611` | PAUSED |
| RAW \| Prospecting \| Main \| 7-Creative Portfolio | `120248830225130611` | PAUSED |

## 2.2 The live ad set — `120249646492400611`

Name: `V81 | US 18-65 broad | FB+IG only | Purchase | no AN/WA/Threads`

```json
{"geo_locations": {"countries": ["US"], "location_types": ["frequently_in","home"]},
 "age_min": 18, "age_max": 65,
 "publisher_platforms": ["facebook","instagram"],
 "facebook_positions": ["feed","video_feeds","marketplace","story","search","facebook_reels","profile_feed"],
 "instagram_positions": ["stream","story","explore","reels","profile_feed","ig_search"],
 "device_platforms": ["mobile","desktop"],
 "targeting_automation": {"advantage_audience": 1}}
```

| setting | value |
|---|---|
| optimization_goal | `OFFSITE_CONVERSIONS` |
| billing_event | `IMPRESSIONS` |
| destination / conversion location | `WEBSITE` |
| promoted_object | `{pixel_id: 622400256897443, custom_event_type: "PURCHASE"}` |
| attribution | 7-day click + 1-day view |
| is_dynamic_creative | `false` |
| bid strategy | Highest volume (`LOWEST_COST_WITHOUT_CAP`), no cap |

**Meta narrows this further on its own** — `effective_facebook_positions` routinely drops
`video_feeds`, and `effective_instagram_positions` drops `explore`. That is Meta's choice, not a
failed write. Compare `effective_*` against requested and report the delta rather than retrying.

## 2.3 All 14 ads in V81

| ad | id | creative | status | spent |
|---|---|---|---|---|
| **UFO control (end card)** | `120249654283880611` | `1356499863310834` | **ACTIVE** | $220.48 |
| **UFO banner (offer @0.0s)** | `120249671503420611` | `1065147595893626` | **ACTIVE** | $63.43 |
| CUPID-R2 static save70 FINAL | `120249654289640611` | `2138899159993629` | PAUSED | $89.07 |
| LOOKUP static save70 FINAL | `120249654288290611` | `1068078835624142` | PAUSED | $19.11 |
| CUPID-R2 static save70 r2 | `120249648313090611` | `1602876511241208` | PAUSED | $0 |
| UFO video b2g1 | `120249646494080611` | `953501277773660` | PAUSED | $0 |
| UFO video save70 r4 | `120249648559340611` | `1570562491483849` | PAUSED | $0 |
| UFO video save70 | `120249648021960611` | `1095709386226097` | PAUSED | $0 |
| LOOKUP static save70 r2 | `120249648312470611` | `1389976239777840` | PAUSED | $0 |
| LOOKUP static save70 | `120249648022840611` | `949804804042637` | PAUSED | $0 |
| CUPID-R2 static save70 | `120249648023950611` | `1810337963460700` | PAUSED | $0 |
| CUPID-R2 static b2g1 | `120249647364390611` | `1783829736131871` | PAUSED | $0 |
| LOOKUP static b2g1 | `120249646495230611` | `1037709372360910` | PAUSED | $0 |
| V47-05 static b2g1 | `120249646496130611` | `1374909448090236` | PAUSED | $0 |

**Deleted:** `120249662229150611` — duplicate of the banner ad, removed Aug-27 21:28 PDT.

## 2.4 V82 spare campaign

| object | id | state |
|---|---|---|
| campaign | `120249669199670611` | PAUSED, $100/day configured, never spent |
| ad set | `120249669203280611` | PAUSED, `Advantage+ Audience ON`, mirrors V81 targeting |
| ad | `120249669207010611` | PAUSED, creative `1065147595893626` (same banner creative) |

Built as a standalone home for the recut, then superseded by the decision to run the banner inside
V81 for a same-ad-set comparison. Retained as a spare.

---

# PART 3 — The creative

## 3.1 What the live ads actually are

Both live ads are the **same 30.1s video**: `UFO-V34`, a "leaked interview" concept. A hooded,
unidentified source in a dark room describes building a pheromone blend as a skin-chemistry test;
intercut with Venice Beach footage of women turning to look as a man passes. Ends on the product.

- **Control** — offer appears only on the **end card**, at ~26s (≈86% into the runtime).
- **Banner** — a two-tone lockup, red `70% OFF` + cream `TODAY ONLY`, **pinned from frame 0 to
  frame 767 (25.57s)**, then fades as the end card takes over. Offer therefore on screen for the
  entire runtime.

Both ship **native 4:5 (1080×1350) and 9:16 (1080×1920)** masters — separately rendered, never
derived. `adapt_to_placement` is OPT_OUT so Meta performs no reformatting; supplying both natives is
mandatory, not optional.

**Masters:** `brands/raw-chemistry/output/ufo-banner-2026-08-27/masters-{4x5,9x16}/`
**Uploaded video IDs:** `2005163350167527` (4:5) · `2170230443928592` (9:16)
**Verification:** 30.100s, 30fps, faststart confirmed by atom order (`ftyp moov free mdat`),
−14.46 LUFS, and **decoded audio PCM sha256 identical to the control** — proving the edit is
video-overlay-only.

## 3.2 Ad copy — verbatim, identical on both ads

**Primary text:**
```
Most cologne is built to smell good for an hour and then quietly quit on you.

RAW 27™ is pheromone-infused — the formula behind 500,000+ bottles since 2016.

★★★★★ 4.8/5 · 12,400+ reviews · Free shipping · 60-day guarantee
Get 70% off — today only.
```
**Headline:** `Your Cologne Quit Two Hours Ago`
**Description:** `70% off today only. Free shipping.`
**CTA:** `SHOP_NOW` · **Display link:** `RAWCHEMISTRY.COM`
**Destination:** `https://rawchemistry.com/pages/raw-pheromone-cologne`

Copy was recovered by **fetching the live ad's own preview iframe and parsing its payload** — not
reconstructed from documentation. Docs drift; the served ad does not.

## 3.3 Creative configuration lock (every creative in this account)

- All **17 Advantage+ creative enhancements `OPT_OUT`** via `degrees_of_freedom_spec` sent alone as
  a JSON-encoded string: `image_touchups, text_optimizations, image_brightness_and_contrast,
  image_templates, image_animation, image_uncrop, add_text_overlay, media_liquidity_animated_image,
  adapt_to_placement, site_extensions, profile_card, enhance_cta, description_automation,
  catalog_feed_tag, product_extensions, cv_transformation, inline_comment`
- **`self_ai_disclosure` omitted entirely** — never OPT_IN, never OPT_OUT. Immutable after creation.
- **Dark posts** via `object_story_spec` — nothing reaches the page timeline.
- `placement_videos`: 4:5 as the unscoped fallback, 9:16 scoped to FB story/reels + IG story/reels.
  Exactly one unscoped entry is required by the API.

**Traps:** `music` is not a valid feature name and rejects the whole call. `standard_enhancements`
is deprecated. Validation stops at the first bad name, so the accepted 17 are expensive to
re-derive — reuse the list.

---

# PART 4 — Every ad day, to the hour

## 4.1 Campaign daily

| day | dow | budget | spend | impr | reach | freq | CPM | CPC | ATC | $/ATC | IC | purch | revenue | ROAS | CAC |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| **Aug-27** | Thu | $100 | $134.48 | 957 | 723 | 1.32 | **$141** | $3.36 | 22 | **$6.11** | — | 3 | $218.90 | 1.63 | $44.83 |
| **Aug-28** | Fri | $100 | $104.27 | 439 | 315 | 1.39 | $238 | $2.67 | 17 | **$6.13** | — | 3 | $218.90 | **2.10** | $34.76 |
| **Aug-29** | **Sat** | **$175** | ~$151+ | 675+ | — | — | $266 | $3.06 | 13 | **$11.60** | — | 1 | $99.00 | **0.66** | $150+ |

Aug-29 was still running at time of writing (18:35 PDT, 77% elapsed, $147.48 at ad level).

**Campaign lifetime: $387.45 · 50 ATC @ $7.75 · 10 IC · 7 purchases · $536.80 · ROAS 1.39 · CAC $55.35.**

## 4.2 Aug-27 hourly (Thu, 3 ads, $100 budget)

| hr PDT | spend | impr | ATC | purch |
|---|---|---|---|---|
| 06 | $20.22 | 94 | 1 | — |
| 07 | $17.88 | 77 | 2 | — |
| 08 | $8.65 | 51 | 3 | **1** |
| 09 | $16.63 | 87 | 2 | — |
| 10 | $13.92 | 107 | 4 | **1** |
| 11 | $6.48 | 76 | 4 | **1** |
| 12 | $5.90 | 53 | 1 | — |
| 13 | $6.31 | 55 | — | — |
| 14 | $5.50 | 73 | 2 | — |
| 15 | $4.20 | 38 | — | — |
| 16 | $9.08 | 56 | 1 | — |
| 17 | $1.56 | 40 | — | — |
| 18 | $2.85 | 35 | — | — |
| 19 | $5.48 | 51 | 2 | — |
| 20 | $5.36 | 42 | — | — |
| 21 | $3.72 | 21 | — | — |
| 22 | $0.74 | 1 | — | — |

Delivery began ~06:00. **All 3 purchases landed 08:00-11:59.** Spend spread broadly.

## 4.3 Aug-28 hourly (Fri, 2 ads, $100 budget)

| hr PDT | spend | impr | | hr PDT | spend | impr |
|---|---|---|---|---|---|---|
| 00 | $1.85 | 13 | | 12 | $9.37 | 24 |
| 01 | $1.88 | 2 | | 13 | $3.51 | 22 |
| 02 | $0.08 | 3 | | 14 | $5.31 | 29 |
| 03 | $0.06 | 2 | | 15 | $6.83 | 23 |
| 04 | $0.47 | 7 | | **16** | **$16.41** | 37 |
| 05 | $2.39 | 14 | | 17 | $6.63 | 40 |
| 06 | $2.22 | 9 | | 18 | $6.82 | 27 |
| 07 | $7.77 | 15 | | 19 | $7.28 | 20 |
| 08 | $3.48 | 15 | | 20 | $1.37 | 18 |
| 09 | $0.76 | 10 | | 21 | $6.13 | 33 |
| 10 | $1.57 | 7 | | 22 | $3.67 | 27 |
| 11 | $4.90 | 16 | | 23 | $3.51 | 26 |

**Spend spread almost flat across 24h.** Only $20.20 (19% of day) by 09:00.
Control-ad ATCs: **9 of 10 landed at or after 14:00.** Biggest hour 16:00.

*Why so slow in the morning:* Aug-27 overdelivered to $134.48 (+34%). Meta balances across a
~weekly window, so Aug-28 was throttled early to repay it.

## 4.4 Aug-29 hourly (Sat, 2 ads, $175 budget) — through 14:59

| hr PDT | ET | spend | CPM | LPV | ATC | purch |
|---|---|---|---|---|---|---|
| 00 | 03 | $3.56 | $237 | 2 | 1 | — |
| 01 | 04 | $1.65 | $183 | 2 | — | — |
| 02 | 05 | $1.72 | $156 | — | — | — |
| 03 | 06 | $5.43 | $319 | 2 | — | — |
| 04 | 07 | $9.96 | $255 | 1 | 1 | **1** |
| 05 | 08 | $9.62 | $196 | 2 | 1 | — |
| **06** | **09** | **$21.62** | $305 | **6** | 1 | — |
| **07** | **10** | **$15.04** | $301 | **4** | **2** | — |
| 08 | 11 | $11.50 | $359 | 2 | 1 | — |
| 09 | 12 | $6.45 | $195 | 3 | 1 | — |
| 10 | 13 | $8.57 | $245 | 1 | — | — |
| 11 | 14 | $3.16 | $316 | — | — | — |
| 12 | 15 | $6.61 | $264 | — | — | — |
| 13 | 16 | $2.30 | $121 | — | — | — |
| 14 | 17 | $2.70 | $159 | — | — | — |

**00:00-09:59 = $86.55 → 24 LPV, 8 ATC, 1 purchase.**
**10:00-14:59 = $23.34 → 1 LPV, 0 ATC, 0 purchase.**

The 06:00-07:00 block took 51% of the day's early budget at the **highest CPMs of the day**
($305/$301 vs $121-196 later). That was **not** irrational: those are 09:00-11:00 Eastern, and the
morning produced every conversion. Meta optimises for predicted purchasers, not cheap impressions,
and paid a premium for the hours that convert.

**Caution recorded:** the 13:00-16:00 stretch looked like a dead zone and was then followed by
2 more control ATCs by 16:42. **Dayparting cannot be judged intraday.**

## 4.5 Per-ad lifetime

| ad | status | spend | impr | CTR | $/LPV | LPV | $/ATC | ATC | purch | revenue |
|---|---|---|---|---|---|---|---|---|---|---|
| UFO control | ACTIVE | $220.48 | 934 | 7.39% | $3.80 | 58 | **$8.48** | 26 | **5** | $377.85 |
| UFO banner | ACTIVE | $63.43 | 308 | **9.09%** | **$3.73** | 17 | **$7.05** | 9 | 1 | $59.95 |
| CUPID-R2 static | PAUSED | $89.07 | 712 | 3.93% | $4.05 | 22 | $6.36 | 14 | 1 | $99.00 |
| LOOKUP static | PAUSED | $19.11 | 132 | 3.79% | $4.78 | 4 | $6.37 | 3 | 0 | — |

## 4.6 Per-format CPM (Aug-27, the only day all formats ran)

| ad | format | spend | impr | **CPM** | $/ATC |
|---|---|---|---|---|---|
| CUPID-R2 | static | $89.07 | **712** | **$125** | $6.85 |
| LOOKUP | static | $19.11 | 132 | **$145** | $6.37 |
| UFO | video | $25.20 | 110 | **$229** | $4.20 |

**Video costs ~1.8× static per impression.** 84% of Aug-27's impressions came from statics, which
is why that day's blended CPM was $141 — the cheapest of the three despite the highest spend.

## 4.7 Every funnel order (price-matched, PDT)

| order | timestamp PDT | amount | product | day |
|---|---|---|---|---|
| #164595 | Aug-27 08:18 | $59.95 | single | Thu |
| #164597 | Aug-27 10:06 | $99.00 | 3-pack | Thu |
| #164598 | Aug-27 11:05 | $59.95 | single | Thu |
| #164608 | Aug-28 15:23 | $59.95 | single | Fri |
| #164610 | Aug-28 18:22 | $99.00 | 3-pack | Fri |
| #164612 | Aug-28 22:34 | $59.95 | single | Fri |
| #164613 | Aug-29 04:52 | $99.00 | 3-pack | Sat |
| | | **$536.80** | | |

**10 consecutive exact reconciliations** between Meta-reported revenue and paid Shopify orders.

**Identify funnel orders by PRICE.** Shopify reports **every one of these as `firstVisit: direct,
lastVisit: direct`** with no UTM, because the funnel converts through **shop.app / Shop Pay, which
strips the referrer**. Non-funnel orders on the same days ($71.00, $67.50, $36.50, $34.50, $36.75)
are other products/channels.

## 4.8 Historical: FREE vs honest offers (90-day, pre-V81)

| | FREE ads | honest-offer ads |
|---|---|---|
| spend | $425.32 | $139.57 |
| impressions | 10,159 | 1,059 |
| CPM | **$41.87** | $131.79 |
| cost/LPV | $1.49 | $4.65 |
| revenue | $73.00 | $218.90 |
| **ROAS** | **0.17** | **1.57** |
| **revenue per LPV** | **$0.26** | **$7.30** |

Honest offers cost **3.1× the CPM** and returned **9.2× the revenue per dollar** and **28× the
revenue per landing page view**.

---

# PART 5 — Chronology: what happened, when, and why

## Aug-26 and earlier — the pre-history

Eleven campaigns had run (V76-V80, Prospecting, Test, Scale) on a funnel later proven broken.
Symptoms: cost per ATC $14-114, several days with zero purchases, and a Shopify attribution report
showing "Facebook/Instagram 0" in the last 16 orders — which was later shown to be **Shop Pay
stripping the referrer**, not an absence of ad-driven sales. Everything was paused to reach one
clean test.

## Aug-27

| when | what | why |
|---|---|---|
| ~04:45 | V81 built: 3 ads (UFO video, LOOKUP static, CUPID-R2 static), CBO $100/day, save70 arm | Clean single-variable test after the funnel fixes |
| ~06:00 | Delivery begins | |
| 08:18 / 10:06 / 11:05 | Three purchases land — $59.95 / $99.00 / $59.95 | Reconciled exactly to Meta's $218.90 |
| 13:33 | Banner recut added to V81 ad set as `120249662229150611`, paused | Operator: "add it to the campaign but keep it paused" |
| 19:27-19:28 | V82 campaign + ad set + ad created, all paused | Operator: "put it into its own campaign" |
| 19:49 | V82 ad set corrected: `advantage_audience` 0 → **1** | Operator corrected me — A+A must stay ON; "no AI flags" means creative AI |
| 21:28 | Duplicate ad `120249662229150611` **deleted** | Same creative existed twice after two instructions |
| 21:34 | **CUPID-R2 and LOOKUP statics PAUSED** | Cost-per-ATC: UFO $4.20 vs $6.85/$6.37. CUPID-R2 had taken 66% of spend on the worst CTR |
| 21:34 | Banner ad `120249671503420611` created in V81 ad set, activated 21:35 | To run banner vs control in the **same ad set** — one variable |
| 21:37 | Meta approves the banner ad | |
| day close | **$134.48, 22 ATC @ $6.11, 3 purchases, ROAS 1.63** | |

## Aug-28

| when | what |
|---|---|
| all day | Two ads only: UFO control + UFO banner. Budget $100 |
| morning | Very slow — $20.20 by 09:00 (19% of day). **Cause: repaying Aug-27's +34% overdelivery** |
| 15:23 / 18:22 / 22:34 | Three purchases — $59.95 / $99.00 / $59.95 |
| — | **9 of the control's 10 ATCs landed at or after 14:00** |
| day close | **$104.27, 17 ATC @ $6.13, 3 purchases, ROAS 2.10** — best day of the three |

## Aug-29

| when | what |
|---|---|
| **02:20** | **Campaign budget raised $100 → $175/day** via `ads MCP server`, actor Brett Messinger. **Not done in-session. Unconfirmed.** |
| 04:52 | One purchase, $99.00 |
| 06:00-07:59 | 51% of early budget spent at the day's highest CPMs — but these hours produced the conversions |
| ~10:00 onward | Conversions stop; $23 spent 10:00-15:00 for 1 LPV |
| ~16:00-16:42 | Two more control ATCs land — the "dead afternoon" read was premature |
| 18:35 | $147.48 spent, 1 purchase, control ROAS 0.78 and falling |
| — | Operator identifies **Aug-29 is a Saturday** — a second variable |
| — | Operator rejects spending further to disambiguate; agreed to revert to $100 |

---

# PART 6 — Everything paused, with reasoning and retrospective

| what | when | reasoning at the time | retrospective |
|---|---|---|---|
| **CUPID-R2 static** | Aug-27 21:34 | $6.36/ATC vs UFO's $4.20; **66% of day-1 spend** on the worst CTR (3.93%) | Defensible on the chosen metric. But it was buying impressions at **$125 CPM** and never got worse than $6.36/ATC — the control later hit $15.90 |
| **LOOKUP static** | Aug-27 21:34 | $6.37/ATC, worst CTR, 0 purchases on $19.11 | Had briefly shown the account's **best $/LPV ($3.55)** at ~$14 spend, which inverted as the sample grew — a small-sample mirage I initially reported as promising |
| **Duplicate banner ad** `120249662229150611` | Aug-27 21:28 | Same creative in two places | Correct; my own sequencing error created it |
| **b2g1 arm** (4 ads) | pre-launch | Operator selected the save70 arm | Held as the week-2 arm test |
| **r2/r4/superseded** (6 ads) | pre-launch | Earlier renders replaced | — |
| **11 older campaigns** | Aug-26/27 | Reduce to one clean test | — |
| **V82 campaign** | never activated | Banner ran inside V81 instead for a same-ad-set comparison | Correct — a separate campaign would have been a dirtier test and doubled spend |

**The consequential one.** Pausing both statics left the campaign with **two cuts of one video**.
Blended CPM went **$141 → $238 → $266** and impressions fell **957 → 439 → 269**. Statics were the
cheap-reach layer. This did **not**, however, explain the Aug-29 spend pattern — see Part 8.

---

# PART 7 — The funnel: every fix, with verification

| # | problem | detail | fix | verified |
|---|---|---|---|---|
| 1 | **Checkout hid the offer** | Shopify mobile renders the order summary **collapsed** — a cold buyer saw only "Order summary" and a number. No product, no quantity, no discount | Anchor prices + automatic discounts so the saving appears on the payment screen | Rendered checkout in a real mobile browser |
| 2 | **Anchor pricing** | — | 3-pack `67395347546416` **$335.85 → $99.00** (−$236.85); single `67395347513648` **$99.00 → $59.95** (−$39.05) | API readback + live checkout |
| 3 | **Quantity overcharge** | Discounts were fixed-amount with `appliesOnEachItem:false` — discount subtracted once per order while the anchor multiplied per unit. **3-pack ×2 charged $434.85 instead of $198.00** | Set `appliesOnEachItem: true` on both | **6 cart shapes** re-tested: 3-pack ×1/×2/×3 = $99.00/$198.00/$297.00; single ×1/×2 = $59.95/$119.90; mixed = $158.95. **No customer was ever affected** — all funnel orders qty 1, no qty selector on the page |
| 4 | **Internal name leaking to buyers** | Checkout displayed **`RAW REVEAL V110 - 3 BOTTLES + FREE ROLL-ON (-$236.85)`** — Shopify prints the discount *title* verbatim | Renamed to `70% Off Today - 3 Bottles + Free Roll-On` and `Today's Price - Single Bottle`; **`tools/raw-offer-pricing.mjs` corrected** so a revert can't reintroduce them | Order summary expanded in a real browser: `v110`/`RAW Reveal` absent |
| 5 | **Shipping label contradiction** | Drawer promised FREE EXPRESS; checkout said `Economy` | Delivery profile **`132870603056`**, holding only the two funnel variants, rate named `FREE Express Shipping (2–4 days)` | `node tools/raw-shipping-label.mjs verify` |
| 6 | **Stale offer badges** | `UP TO 70% OFF` contradicted the flat 70% claim | Removed | **0 remaining**; 8 CTAs read `GET 70% OFF TODAY` |
| 7 | **Pixel fired too late** | PageView only after 30s or interaction | `sendBeacon` PageView at a fixed **2,500ms**, `eid` dedup key, `_fbp`/`_fbc` set incl. `fbclid` capture. Original 30s runtime loader left intact | Real headless browser: **one PageView at 2.73s, no interaction, zero JS errors** |
| 8 | **Page speed regression** | A 3-second tracking timer shipped and crashed live PSI to **71 / TBT 1,540ms** | Reverted within minutes | **PSI mobile 99/100/100/100, TBT 0ms; desktop 100/100/100/100; Core Web Vitals field data PASSED** (LCP 2s, INP 162ms, CLS 0.02) |

**Discount node IDs:** 3-pack `1616747659568` · single `1616747692336`. Both ACTIVE, no end date.
**24 code discounts exist and do NOT stack** on these automatics — verified.

**Funnel state now:** click → LP (PSI 99, pixel at 2.5s) → drawer (offer visible) → checkout
(discount visible, correct name, free express labelled) → Shop Pay.

---

# PART 8 — The open question: Saturday vs budget

**Aug-29 broke a two-day pattern.** $/ATC $6.11 → $6.13 → **$11.60**. ROAS 1.63 → 2.10 → **0.66**.

**Two variables changed simultaneously:**
1. Campaign budget **$100 → $175** at 02:20 PDT
2. **Thursday/Friday → Saturday** — the campaign's first weekend day

## What the data can and cannot say

**Purchase count proves nothing.** At the Aug-27/28 rate (2.33/day), P(observing ≤1 purchase on a
given day) = **32%**. One-versus-three is unremarkable.

**Cost-per-ATC is the real signal** — 13 events, not 1 — and it roughly doubled.

**A single ad DOES absorb budget linearly.** Hour-matched, the control ad only, hours 00:00-10:59
on both days:

| | Aug-28 ($100) | Aug-29 ($175) | ratio |
|---|---|---|---|
| spend | $8.98 | $85.49 | **9.5×** |
| impressions | 32 | 310 | **9.7×** |
| **CPM** | **$281** | **$276** | **0.98× — flat** |
| ATC | 0 | 6 | |

So budget does **not** inherently inflate cost. That kills the simplest explanation.

**No weekend baseline exists.** The account's only prior Saturday is **Aug-08: $5.43 spend, 1 ATC**.
Everything material changed since — creative, offer, checkout fix, pixel. Pre-Aug-27 days ran
different creative on a broken funnel ($/ATC $14-114). **Nothing to compare against.**

## Agreed plan

- **Revert budget $175 → $100.** At 18:35 the campaign had spent $147.48, already past $100, so this
  also halts remaining Saturday spend. Saves ~$40 tonight + ~$85 Sunday ≈ **$125**.
- **Do not read Sunday.** It differs from Thu/Fri on both weekend *and* budget.
- **Monday Aug-31 at $100 vs Thursday/Friday at $100** is the clean like-for-like weekday read.
- **Re-test $175 later on a weekday, with a wider creative base.**
- **Begin accumulating a day-of-week baseline** — the account has never had one.

*Rationale for not spending to disambiguate:* the same answer is available later for the same money,
on a weekday, with better creative. Buying it now on a ROAS-0.66 day is paying a premium for a
tidier chart.

---

# PART 9 — Banner vs control: where the test actually stands

| | control (end card) | banner (offer @ 0.0s) |
|---|---|---|
| spend | $220.48 | $63.43 |
| impressions | 934 | 308 |
| CTR | 7.39% | **9.09%** |
| $/LPV | $3.80 | **$3.73** |
| **$/ATC** | $8.48 | **$7.05** |
| ATC | **26** | 9 |
| initiated checkout | **7** | 1 |
| purchases | **5** | 1 |
| revenue | $377.85 | $59.95 |

**Both cleared the 10-ATC gate cumulatively.** The banner leads every cost metric; the control leads
purchases — but has had **3.5× the delivery**, so the purchase gap partly reflects budget share.

**Day by day the story flipped repeatedly:**

| | Aug-28 $/ATC | Aug-29 $/ATC |
|---|---|---|
| control | **$6.36** | $15.90 |
| banner | $6.98 | **$6.77** |

**Not resolvable while Aug-29 is confounded.** Carry into the Monday read.

**What the competitor evidence predicted:** short ads (≤35s) place the offer at a median **0.0s**,
5/5 within five seconds; GODA's longest-running live ad pins its offer *and* a `Buy 2, Get 1 FREE`
badge from frame 0 and never removes them. That predicted the banner should win. **Our own money has
not confirmed it.**

---

# PART 10 — Competitive intelligence (Foreplay, 2026-08-28)

Four brands, 250-ad newest-first samples, all creatives opened and measured.

| brand | active | indexed | VIDEO | IMAGE | DCO | kill speed |
|---|---|---|---|---|---|---|
| Hypnosis by Cupids | 452 | 3,244 | **91%** | 9% | 0% | **9.8h / 31h** |
| Goda Perfume | 387 | 8,998 | 8% | 30% | **62%** | mixed |
| IM8 | 232 | 14,494 | 10% | **90%** | ~0% | slow |
| SALT & STONE | **825** | 12,440 | 14% | 23% | **62%** | **barely kills — 93% still live** |

## 10.1 Findings

- **Two operating models, no middle.** Specialists (Cupids video / IM8 static) or DCO operators.
- **Cupids: one concept, ten lengths.** Their ten longest-running live ads share the identical
  headline "Secret to Attention 👉" and are ten different files at **14.88, 38.19, 39.27, 39.83,
  40.75, 46.53, 46.95, 60.67, 74.88, 91.9s**. Live range spans **7.99s–93.18s**.
- **Cupids kills on a schedule, not a curve** — dead ads cluster at exactly **9.8h and 31h**, no
  3/5/7-day tail. Survivors from the same day run 94h+.
- **Cupids launches in bursts** — ~152 ads on Aug-26, 7 on Aug-27.
- **Polish is not the winning variable.** Cupids' survivors are phone-shot UGC: car selfies,
  "testing pheromone cologne on my sisters hot friend", club footage, a screenshot of a review
  captioned "The 500,000+ positive reviews weren't LYING".
- **GODA: the copy carries the sale.** Their DCO static is a **flat offer card** — cream circle,
  wordmark, LAST CHANCE, 50% OFF, two clipart alarm clocks. No product, no model. Attached to
  ~**600 words of first-person advertorial** signed "Michelle D."
- **GODA's video uses AI-generated dolls** with cracked porcelain skin as a literal metaphor for
  crepey skin, smoothing over a day-1→60 arc. No model, no shoot, no likeness rights.
- **GODA gives the discount a reason-why.** Their longest-running live ad: *"We Made Too Much Body
  Oil / PLEASE HELP US EMPTY OUR WAREHOUSE"* + *"Buy 2, Get 1 FREE"*, both pinned from 0.0s, over a
  spoken apology: *"I told you that was the best price you'd ever get — and then we overproduced."*
  **Ours asserts `70% OFF TODAY ONLY` with nothing behind it.**
- **GODA statics are blunt pain-first** — "F\*CK BEING 'JUST FRIENDS'", "Need A Relationship Spark?",
  "Do Not Try This IF you enjoy being the only one making an effort" — plus hard price anchoring
  (**$78 → $14/bottle**) and review badges.
- **Salt & Stone** — clean product photography, press borrowing ("AS SEEN IN MARIE CLAIRE"),
  scarcity ("Mini set. Big waitlist." / "SOLD OUT 3X").
- **IM8** — restrained editorial, research-led ("You're Allowed To Stop Taking 12 Supplements").
- **30 seconds — our only length — is the length neither leader leans on.**

## 10.2 Companion pages — confirmed

| brand | model | pages |
|---|---|---|
| **Cupids** | fake editorial | `Gents Secrets` (categorised **Magazine**), `Hypnosis by Cupids` (categorised **Musician/band**, no website, **452 active vs main page's 199**) |
| **GODA** | invented personas | `Emily The Fragrance Girl`, `Steve The Perfumer`, `Over 40 & Fabulous` |
| **IM8** | **real** authorities | `Dr. James DiNicolantonio`, `Tim Gray`, `David Beckham` (blue-verified), `New York Post`, `Athleats Nutrition` — plus 4 legitimate own-brand pages |
| Salt & Stone | single page | — |

Satellite pages demonstrably outrun motherships. All three flavours are available options; the
choice is the operator's.

## 10.3 DCO — investigated and closed

Foreplay's `display_format: DCO` means **any multi-asset creative, including plain placement
customization**. Proven symmetrically:

- Salt & Stone "pools": **720×1280 + 720×900** pairs, 2 of 3 identical in duration to six decimals.
- GODA "pools": **byte-identical duplicate cards** across 4 opened ads.
- **Our own ads: 11 of the newest 25 carry the DCO label.** The UFO ad's two cards measure
  720×1280 and 720×900 at identical 30.164042s with **identical audio sha256**.

**There is no DCO gap. We have shipped the mechanism since V76.** Do not reopen without new
primary evidence.

**Boards published:**
`http://15.204.83.60/raw-chemistry/competitor-creatives/` · `/raw-chemistry/offer-timing/`

---

# PART 11 — Everything learned

Full text in `LEARNINGS.md`. Complete list, grouped.

## 11.1 Offer and price

1. **"FREE" trains Meta to find people who want free things.** See Part 4.8. Cheap CPM is a
   *warning*, not a win — a proposed rule to kill ads above $120 CPM would have killed every
   profitable ad and protected every unprofitable one.
2. **Higher price changes what a "bad" CAC is.** AOV $72.97. CAC $43 against $73 AOV is a different
   business from CAC $20 against $30. **But breakeven CAC is still unknown without COGS.**
3. **An offer with a reason behind it beats a naked number** (GODA evidence, untested for us).

## 11.2 Funnel

4. **The offer must be visible at the moment of payment.** *Caveat carried:* the famous "3 of 3 vs
   5.3%" comparison used a **double-counted denominator** — IC fired twice (drawer + Shopify).
   Real Aug-24 funnel: 129 LPV → 9 buy-button clicks → 1 purchase. Direction holds, magnitude was
   inflated.
5. **Promise-to-page continuity is a conversion gate.** Stale badges, contradictory shipping labels,
   hidden discounts — all fixable in an afternoon and worth more than a new creative.
6. **Tracking must be verified in a real browser.** Three false-negative traps: waiting <31s without
   interacting; parsing `ev=` from a query string when Meta posts URL-encoded bodies; comparing
   against a non-deferred page.
7. **A dev-theme performance pass is not a live-site gate.**
8. **Fixed-amount discounts multiply wrong** — test qty 2 and mixed carts.
9. **Anything named that reaches Shopify is buyer-visible** — discounts, shipping rates, delivery
   profiles. No versions, run IDs or codenames.
10. **The operator browses in CAD; the store and campaign are USD.** Convert before diagnosing.

## 11.3 Creative

11. **Format is not the variable; the specific creative is.** We were wrong in both directions —
    "video beats statics" (wrong on the metric that matters: statics bought LPVs at $1.53 vs $2.91
    over 90 days) and then the UFO video beat both statics in V81.
12. **Put the offer on screen from second zero** — frame-verified across 40 competitor breakdowns.
    *Being tested; our own money has not confirmed it.*
13. **One concept, many lengths** — Cupids' ten survivors, 7.99s–93.18s.
14. **Polish is not what wins.**
15. **The copy can carry the sale** — GODA's flat offer plate + 600-word advertorial.
16. **Pain-first, named explicitly.**

## 11.4 Measurement and decision-making

17. **Add-to-cart is the decision currency, not purchases.** ~20 ATC/day vs ~3 purchases.
18. **Small samples produce confident mirages — four times in one week:** LOOKUP's $3.55/LPV; the
    banner's 29.6% CTR on 27 impressions; a "dead afternoon" that revived; a "$16.49/ATC" alarm that
    was a partial-day artifact.
19. **Never compare a partial day to a full day.** Cost-per-outcome inside a partial day is not a
    rate — impressions land before conversions.
20. **A token's prefix does not tell you its lifetime** — trust `expires_in`, never `shpat_`.
21. **Budget scales linearly for a single ad** — 9.5× spend → 9.7× impressions, CPM flat.
22. **The learning phase is unreachable at this budget** (~17 purchases/wk vs ~50). Never argue
    from it.
23. **Meta overdelivers the daily budget** (+34% observed) and repays across the week. Spend pacing
    is a **multi-day** system.
24. **Verify by readback; never trust the write response.** `updated_fields` echoes the request.
25. **Reconcile revenue against the store by price** — Shopify says `direct` for everything.
26. **Check the activity log before explaining any delivery change.**
27. **Narrowing a campaign raises blended CPM** — but does not explain day-to-day pacing.

## 11.5 Platform mechanics

28. **Advantage+ Audience ≠ Advantage+ Creative.** Audience is a delivery control, stays **ON**.
    Creative mutates the ad — all 17 **OPT_OUT**.
29. **Enhancements default ON** if `degrees_of_freedom_spec` is omitted.
30. **Placements default to Advantage+**, which delivered us to Audience Network rewarded video,
    WhatsApp status, Threads, Messenger. Pin FB+IG explicitly.
31. **`advantage_audience: 1` demotes age limits to suggestions.**
32. **`self_ai_disclosure` is immutable after creation.**
33. **Numbers in documentation carry their original context** — an `age_min: 25` was lifted from a
    doc describing a *UI dropdown limitation* and applied as strategy.
34. **Foreplay's DCO label = multi-asset creative**, including placement customization.

## 11.6 Research method

35. **Longevity is a proxy for profitability, not a measurement.** Foreplay exposes no ROAS.
36. **Competitor ad counts are not creative output.**
37. **Model-generated timestamps need frame verification** — Gemini writes `end = runtime` for
    anything that reads persistent. One badge claimed 0.0→31.209s; pixels show it gone by 6.9s.

---

# PART 12 — Mistakes made, and how each was caught

Recorded because the pattern repeats and recognising it early is worth more than any single finding.

| # | mistake | how it was caught | rule adopted |
|---|---|---|---|
| 1 | Claimed the LP had no tracking | Repo had already documented and corrected this once | Primary source before assertion |
| 2 | "Video beats statics 2:1" | Inherited from a handoff; 90-day data inverted it on cost/LPV | Never inherit an unmeasured claim |
| 3 | Picked the wrong static set from filenames | Operator: "that's the old run" | Folder names lie; sort by mtime |
| 4 | `age_min: 25` from a UI-limitation doc | Operator: "18-65 is fine why did you do 25" | Read the surrounding sentence |
| 5 | AI enhancements left defaulted ON | Operator asked "no creative enhancements?" | Set every field explicitly |
| 6 | Swept `advantage_audience` to 0 | Operator corrected — A+A must stay on | "No AI flags" = creative AI |
| 7 | Proposed a CPM>$120 kill rule | Operator: "we aren't saying free anymore" | CPM describes *who*, not *how well* |
| 8 | Shipped a 3s tracking timer → PSI 71 | Live PSI re-measure | Dev-theme pass is not a live gate |
| 9 | Prevalence-as-proof on DCO — **twice** | Operator asked "is it genuinely more effective?" | Presence ≠ performance |
| 10 | Said `shpat_` = permanent, "revoked not expired" | The exchange returned `expires_in: 86353` | Trust `expires_in`, not the prefix |
| 11 | Blamed sandbox for `exit 144` | It was `pkill -f` matching its own shell | Never `pkill -f` a pattern in your own argv |
| 12 | "The CLI hangs on auth" | `app list` doesn't exist in CLI 4.6.1 — subcommand error misread | Verify the command exists before diagnosing |
| 13 | Blamed campaign narrowing for front-loaded spend | Operator: "yesterday had no statics either" | Verify the cause actually changed |
| 14 | Blamed multi-day pacing for the same thing | Activity log showed the budget had been raised | Check the change log first |
| 15 | "CPM rises with budget" | Hour-matched data showed CPM flat at 9.5× spend | Never compare partial vs full day |
| 16 | Called the afternoon dead | Two ATCs landed an hour later | Dayparts can't be judged intraday |
| 17 | Wrote a decision rule ignoring the weekday | Operator: "it's also Saturday" | Enumerate variables before committing a rule |
| 18 | Proposed spending ~$85 to disambiguate | Operator: "so just waste money lol" | The same answer is often available later, cheaper |

**The dominant pattern: reaching for a plausible mechanism instead of checking whether the
suspected variable actually changed.** Items 13, 14, 15 and 17 are all the same error.

---

# PART 13 — Tools built (all in `tools/`, all runnable)

| tool | what it does |
|---|---|
| `shopify-oauth-paste.mjs` | PKCE paste-back OAuth for a headless host. Defaults to our own app; `--cli` falls back. Writes a `.meta.json` sidecar with real `expires_in` |
| `shopify-token-health.mjs` | Structure + liveness + granted scopes; reports hours remaining, warns <6h. **`life OFFLINE` = permanent** |
| `shopify-token-receive.mjs` | One-shot paste form for an Admin API token — random path, verifies before storing, exits after one success |
| `shopify-admin.mjs` | Generic Admin GraphQL runner |
| `raw-offer-pricing.mjs` | snapshot / apply / revert anchor prices + automatic discounts. **Titles are customer-facing** |
| `raw-shipping-label.mjs` | Creates/verifies the surgical delivery profile |
| `raw-pixel-verify.mjs` | Verifies the Meta event sequence through the funnel |
| `raw-funnel-walk.mjs` · `funnel-walk-audit.mjs` | Full instrumented walk at iPhone viewport |
| `audit-page-tracking.mjs` | Does the page actually fire tracking? Documents the 3 false-negative traps |
| `raw-trust-signal-audit.mjs` | Where does a given string actually appear on the live page? |
| `shopify-theme-file.mjs` | Read theme files into a local directory |
| `history.mjs` | The append-only ledger — `record` / `validate` |
| `brands/raw-chemistry/scripts/analyze_competitor_offer_timing.py` | Offer-timing analysis over Gemini breakdowns |
| `brands/raw-chemistry/scripts/render-ufo-banner-2026-08-27.sh` | Renders a banner master through the house chain |

## Skills created

| skill | covers |
|---|---|
| **`build-meta-ad-objects`** | Every campaign/ad set/ad/creative build. The Advantage+ split, broad targeting, placement lock, dark posts, `self_ai_disclosure`, plus **`scripts/verify-ad-objects.mjs`** which asserts the whole config from readback JSON and exits non-zero on failure |
| **`connect-shopify-admin-api`** | Permanent tokens on a headless host: PKCE paste-back, why the CLI client gives 24h tokens, the empty `redirect_urls` trap, fixing app config via CLI deploy, plus `references/traps.md` indexed by error string |

---

# PART 14 — Blockers (all operator inputs)

1. **COGS / fulfilment / payment fees / refund reserve.** Without it there is no breakeven CAC.
   At $72.97 AOV:

   | contribution margin | breakeven CAC |
   |---|---|
   | 70% | $51.08 |
   | 60% | $43.78 |
   | 50% | $36.48 |
   | 40% | $29.19 |

   Measured CAC has run **$34.76 → $150+**. At 50% margin the account is roughly breakeven; at 40%
   it is losing money; at 70% it has room. **This single number converts every scale decision from
   opinion into arithmetic.**
2. **A true reason-why for the 70%** — overstock, batch sellthrough, packaging changeover, 3-pack
   launch inventory. Must be genuinely true; it is checkable by customers.
3. **Confirm the 02:20 Aug-29 budget change was intentional** — made via MCP, not in-session.
4. **Go-forward budget** — hold $100, or a step plan.

---

# PART 15 — Next actions, in order

1. **Revert budget $175 → $100** *(pending operator go — recommended tonight, saves ~$125)*.
2. Let Sunday run. **Do not read it** — confounded on both weekend and budget.
3. **Monday Aug-31**: full-day $/ATC vs the Thu/Fri $100 baseline. Answers budget-vs-weekend **and**
   resolves the banner test.
4. **Build width** (`ADS-PLAN.md` Phase 1): UFO at **12s and 60s** alongside 30s; revive a static.
   Target **5-8 distinct live ads** before any further budget increase.
5. **Phase 2**: long-form advertorial copy behind a cheap offer plate; reason-why offer variant;
   pain-first sharpening of the next static batch.
6. **Phase 3**: repurpose V82 as a **$50/day testing campaign**, 5-7 new ads/week, promote at 10 ATCs.
7. Only then step budget **+20-30%**, 2-3 days per step, judged on **full-day** $/ATC.

---

# PART 16 — Standing rules (do not relitigate)

- **Advantage+ Audience ON** (`advantage_audience: 1`). **Advantage+ Creative OFF** (17 × `OPT_OUT`).
- Age **18-65**, US, broad. No interests, behaviours, lookalikes, custom audiences.
- **FB + IG only** — forced by `adapt_to_placement: OPT_OUT` and native-master authoring.
- **`self_ai_disclosure` omitted entirely**, forever.
- **Dark posts only.**
- **Verify by readback**, never the create/update response.
- **Judge on cost-per-ATC**; confirm on purchases later.
- **No kill/promote decision below ~10 ATCs per arm.**
- **Never compare a partial day to a full day.**
- **Risk/compliance analysis is pull-only** — the operator will ask for it.
- Record every material change: `node tools/history.mjs record --workstream ads`.

---

## Reproduction commands

```bash
node tools/shopify-token-health.mjs                    # expect: life OFFLINE
node tools/raw-shipping-label.mjs verify               # delivery profile 132870603056
node tools/history.mjs validate                        # ledger integrity
node ~/.codex/skills/build-meta-ad-objects/scripts/verify-ad-objects.mjs < readback.json
node tools/shopify-admin.mjs '{ shop { name currencyCode } }'
```

**Published copy:** `http://15.204.83.60/raw-chemistry/handoff/`
**Source of truth:** this file, in the repo.
