Trafilea PPC HandoverCarlos Carrero · last day Friday 4 September 2026

Trafilea PPC Handover

Carlos Carrero's last working day is Friday 4 September 2026. This page records what he ran, how often, and what stops running when he leaves. Part 0 is the only section a manager has to read.

0. Read this first

EveryoneManager

Nothing on this page fails loudly. No alert fires, no dashboard turns red, no report says "missing". Automations simply stop, promo assets simply keep serving, and experiments simply expire. The gap between something breaking and someone noticing is measured in weeks. That is the whole reason this document exists.

Dated obligations

DateWhat happensOwner
Wed 2 SepWarehouse Clearance push for UK + AU (promo starts 3 Sep)Carlos
Fri 4 SepLast day. The Mac stops running 19 scheduled Trafilea jobs. Four write to Google Ads, but only one (brand-promo-auto-off, 11:00) touches campaigns that are serving
Tue 9 SepWarehouse Clearance push for US + CA (promo starts 10 Sep). The cycle needs two pushes because the script has no per-market start dateUNASSIGNED
Wed 24 SepWarehouse Clearance ends. Assets must be swapped or they keep servingUNASSIGNED
Tue 30 SepVerdict due: SR_AU_WW_BR_ShaperMint_Core_2 Target‑IS vs Manual CPCUNASSIGNED
Wed 7 OctVerdict due: SHM search brand Core_2 USA — Aug2026 (LowCPC_CAP)UNASSIGNED
Fri 23 OctVerdict due: Bra_Products_CPC vs VBB UNASSIGNED

What "verdict due" means. A Google Ads experiment does not wait for a decision. On its end date the arm stops and traffic returns to the original campaign, discarding whatever it learned. Two of the three below are currently winning. If nobody applies them, the account quietly reverts to the worse configuration.

Serving with no end date — act before Friday

Fourteen promo-bearing assets are enabled with no end date at all. An asset with an empty end date serves forever; on Microsoft Ads the redemption date (PromotionEndDate) does not stop delivery, only Scheduling.EndDate does. The full checklist is in reports/handover/data/MITIGATION.md. The ones customers can see today:

AccountAssetWhy it matters
SHM BingCALLOUT "Black Friday Early Access" ×2 Black Friday is three months away
SHM BingCALLOUT "Fall Sale in US" / "Fall Sale in CA" No end date — will outlive the cycle
SHM BingCALLOUT "Autumn Sale in UK"Same
SHM BingCALLOUT "Warehouse Sale" Correct from 3 Sep, but keeps serving past 24 Sep
TK BingCALLOUT "Spring Sale"Six months stale

Four decisions Trafilea owes itself this week

  1. Who runs the bi-weekly promo cycle. One script writes to SHM Bing ×4 markets, TK Bing, SHM Google and TK Google in a single run — so it now spans all three new owners. Splitting it by account is possible but the detection keyword lists are global, and they drift apart by the second cycle. That drift is the documented cause of "EOFY Sale" serving from June into August.
  2. Who consolidates the weekly WoW deck. Fourteen segments, previously one person's Monday. It now splits three ways and has to converge before the deck ships.
  3. Which mutating automations get migrated, and which are switched off on purpose. Four jobs write to Google Ads daily from a laptop that is being returned. brand-promo-auto-off is the one that needs a decision this week; the other three write into paused campaigns and can wait, provided nobody reactivates those campaigns without restarting the miner.
  4. Who re-issues the credentials that authenticate as Carlos. Four n8n workflows keep running server-side but sign in with his tokens; every published report URL sits on his personal Cloudflare account.

1. Scope and ownership

Everyone

Measured against the APIs on 1 September 2026, not from memory.

BrandGoogle AdsMicrosoft Ads
Shapermint Search Brand only — ~41 enabled campaigns across US, CA, UK, AU and OC. Accounts 1037305439 and 9328705246 Everything150000172, 4 markets, 14 active campaigns
Truekind Everything — 17 enabled: Search Brand + NB, Shopping, 2 PMax. 1871711997 Everything141454956
The Spa Dr Everything — 9 enabled: Search Brand + NB, Shopping, Demand Gen. 9662795380 Everything141948660
Revel Beauty Paused, zero campaigns — 141948661

Out of scope, and do not assume otherwise. Shapermint's Google Shopping, Performance Max, Non-Brand Search, YouTube and Demand Gen were never Carlos's. They belong to another team and are absent from this document on purpose. Every Shapermint figure quoted here covers Search Brand and Microsoft Ads only.

Who takes what

BlockIncoming owner
SHM Search Brand — Google, 5 market groupsJuan Paiva
The Spa Dr — Google (Search B+NB, Shopping, Demand Gen)Juan Paiva
Truekind — Google (Search B+NB, Shopping, 2 PMax)Juan Alexander
All Microsoft Ads — SHM ×4, TK, TSD, RevelCassia

Two processes do not split along these lines. The promo cycle and the weekly WoW deck each cross all three owners. Neither has an owner in the table above; both are listed as open decisions in Part 0. Filtering this page to one name will not show them to you, which is exactly the risk.

Built by Carlos, for other people's channels

These monitor or report on channels outside his scope. They need a decision of their own: adopt, or retire.

  • com.ppc.gads-bid-change-history-shm — tracks bid changes on SHM USA Non-Brand Search, Shopping and PMax
  • n8n SHM&TK vs Antares — Daily — covers every SHM channel and is consumed by the wider team, not just Carlos
  • The SHM USA ROAS-drivers report and the shm-pacing-lever skill — both cover Shopping and PMax

2.1 Shapermint — Google Search Brand

Juan Paiva

~41 enabled campaigns across US, CA, UK, AU and OC, in two accounts (1037305439 for US/UK/AU, 9328705246 for Canada). The largest block of spend in this handover.

Reading a campaign name

SR_<country>_[WW_]BR_ShaperMint_<variant>. SR is Search, BR is brand, NB is non-brand (not yours here). The variants each catch a different query shape:

  • Core_2 — the main exact-match brand campaign. The single most important campaign in the account.
  • Modifiers / Modifiers_CPC — brand plus a qualifier ("shapermint reviews", "shapermint discount").
  • Products, Bra_Products, Cami_Products, Swimsuits_Products, Tier2_Products — brand plus a product line.
  • Core_Broad — the broad-match arm. Structurally the weakest performer; see below.
  • Reviews — review intent.

Bidding philosophy — read this before "fixing" anything

Brand runs on Manual CPC and Target Impression Share, managed by hand to roughly 88–90% Search Top Impression Share — not absolute impression share, which sits near 98%. There are deliberately no automated rules on these campaigns. A brand keyword that loses top-of-page to an affiliate or a competitor costs far more than the incremental click, and smart bidding has repeatedly under-bid the defensive positions. Raising this to 100% is not the goal either; the last few points of Top IS are priced irrationally.

Where it stands — 30 days to 31 August, ROAS CDP

MarketSpendvs prior 30d ROAS CDPOrders
USA$82,592−16%10.3912,581
Canada$25,647−24%15.705,581
UK$16,048+12%9.792,387
Australia$10,634+37%16.332,376
Total$135,36711.8022,997

Top Impression Share by campaign

CampaignTop ISLost to rank30d cost
SR_US_BR_ShaperMint_Core_289.7%1.8%$44,738
SR_US_BR_Shapermint_Bra_Products_CPC86.4%7.7%$13,176
SR_US_BR_ShaperMint_Modifiers_CPC84.5%7.5%$13,143
SR_CA_WW_BR_ShaperMint_Modifiers_CPC82.3%8.3%$9,239
SR_UK_WW_BR_ShaperMint_Core_297.0%1.4%$6,318
SR_CA_WW_BR_ShaperMint_Core_Broad74.9%13.3%$6,227
SR_AU_WW_BR_ShaperMint_Modifiers_CPC71.1%17.0%$3,240
SR_US_BR_ShaperMint_Core_Broad52.4%34.7%$2,987

Three live experiments — each needs a verdict

ExperimentTestingEnds
SR_AU_WW_BR_ShaperMint_Core_2 Target Impression Share (control) vs Manual CPC (_manualCPC_V2). Running since 6 Aug with a daily tracking sheet30 Sep
SHM search brand Core_2 USA — Aug2026 A low CPC cap on Core_2 (_LowCPC_CAP). The cap arm holds 90.8% Top IS at $2,701 against the control's 89.7% at $44,738 — it has been the stronger arm on cost per click7 Oct
Bra_Products_CPC vs VBB Target IS vs value-based bidding on Bra_Products. Launched 29 Aug — only Carlos knows it exists23 Oct

The account also carries roughly 25 zombie experiments dated 2020–2024 with end dates in 2037. They belong to old video and audience tests outside this scope. Ignore them; they are noise, not risk.

Open problems and what I would do

  1. SR_US_BR_ShaperMint_Core_Broad is losing a third of its top-of-page to rank (34.7%) at 52.4% Top IS. It is the worst-performing brand campaign in the account and has been for months. Either commit to it with a real bid and tighter negatives, or fold its budget into Core_2 and Modifiers. Half-funding a broad brand arm is the worst of both.
  2. AU Modifiers at 71.1% Top IS, 17% lost to rank, while AU Core_2 sits at 92.8%. Australia is also the fastest-growing market (+37% spend, ROAS CDP 16.33). Modifiers is the obvious next place to buy.
  3. USA and Canada spend fell 16% and 24% month over month while UK and AU grew. Confirm this was deliberate pacing rather than a bid or budget drift — the bid-change history sheet covers SHM non-brand, not brand, so brand changes are not automatically logged anywhere except Notion.
  4. Apply the LowCPC_CAP result rather than letting it expire. It is the clearest efficiency win available and it dies on 7 October by default.

2.2 Shapermint — Microsoft Ads

Cassia

Account 150000172, 4 markets. 14 active campaigns — all Search, all brand. 148 paused Search campaigns and every Shopping, PerformanceMax and Audience campaign are paused as of 1 September.

MSAN — the Microsoft Audience Network — must be disabled on every Search campaign. This is the first check on this account, every time, before looking at anything else. It re-enables itself through UI changes and campaign copies, and it spends Search budget on display inventory at a fraction of the ROAS.

Where it stands — 30 days to 31 August, ROAS CDP

SegmentSpendROAS CDPOrdersNote
Bing Search — USA$15,4308.712,063The core of the account
Bing Search — CA$1,62915.91355Best ROAS in the account
Bing Search — UK$1,3026.85134
Bing Search — AU$92313.38156Underweight for its ROAS
Bing Max — USA$9,1161.67247Now paused
Bing Max — CA$3,8760.9758Now paused — was below break-even
Bing Shopping — USA$7341.0715Now paused
Total$33,0106.103,028

Structure notes

  • Every active campaign runs Enhanced CPC except SR_UK_WW_BR_ShaperMint_Core_Experiment (Max Conversions) and SR_US_BR_ShaperMint_Core_Broad, which reports no bid strategy at all — worth a look.
  • Daily budgets total roughly $9,350 against ~$1,100/day of actual spend. Budget is not the constraint anywhere in this account; bids and impression share are.
  • Of 448 ad extensions in the account library, only 56 are attached to the 14 active campaigns. Library status "Active" does not mean serving.

Open problems and what I would do

  1. Six callouts are serving with no end date, including "Black Friday Early Access" in September and "Autumn Sale in UK". Fix these first — they are visible to customers today. Full list in MITIGATION.md.
  2. Canada and Australia are underweight. ROAS CDP 15.91 and 13.38 against USA's 8.71, on $1,629 and $923 of spend. If the impression share headroom is there, these are the cheapest incremental orders in the portfolio.
  3. Decide whether Bing Max stays dead. It was paused at ROAS CDP 1.67 USA and 0.97 CA. That was the right call on those numbers, but nothing documents whether it is a permanent structural decision or a pause pending a fix.
  4. CDP is the source of truth here. Microsoft-reported ROAS is directional only; it disagrees with Tableau and always has.

2.3 Truekind

Juan Alexander — GoogleCassia — Bing

Google account 1871711997: 17 enabled campaigns across Search Brand and Non-Brand, Shopping and two Performance Max. Microsoft account 141454956: 2 active Search campaigns, everything else paused.

Where it stands — 30 days to 31 August, ROAS CDP

ChannelSpendvs priorROAS CDPOrders
Google Shopping$25,383−31%1.96735
Google Performance Max$20,310+8%1.83554
Google Search$19,410+1%4.781,337
Bing Shopping$6,017−0%0.9598
Bing Search$4,702−14%2.91214

Structure notes

  • Brand Search runs Manual CPC across US and CA (Core_2, Modifiers_CPC, Products_CPC, Coupons, Reviews), with Core_Broad on Max Conversion Value.
  • Non-Brand: SR_US_TK_NB_Bras_2026 (Max Conv Value), SR_US_TK_NB_Competitor (tROAS), plus a broad-match experiment.
  • Two Performance Max campaigns, brand and non-brand, both Max Conversion Value.
  • Two daily negative-keyword automations write to this account — one for NB Search (13:30), one for Shopping NB (08:30). Both target campaigns that are currently paused, so their stopping on Friday costs nothing today. See Part 5.1.

Open problems and what I would do

  1. Bing Shopping at ROAS CDP 0.95 on $6,017. It is below break-even and it is the one line in this portfolio that loses money outright. It has since been paused — confirm that is intentional and permanent rather than an unlogged change.
  2. SR_US_TK_NB_Bras_2026 holds 10.0% Top IS on $4,724 of spend. Either it is bid far below the auction or the keyword set is too broad for its budget. This is the clearest diagnostic to run first on this account.
  3. Google Shopping fell 31% month over month while PMax grew 8% at a lower ROAS CDP (1.83 vs 1.96). If that shift was deliberate it is moving spend the wrong way; if it was not, PMax is absorbing Shopping's inventory on its own.
  4. Brand Core_2 spends almost nothing ($136 in 30 days at 80.5% Top IS) while Products_CPC carries $5,880. Worth confirming the brand structure is splitting queries the way it is supposed to.

2.4 The Spa Dr

Juan Paiva — GoogleCassia — Bing

The smallest account and the one with the clearest open problem. Google 9662795380: 9 enabled campaigns. Microsoft 141948660: 3 active Search campaigns.

Where it stands — 30 days to 31 August, ROAS CDP

ChannelSpendvs priorROAS CDPOrders
Google Discovery (Demand Gen)$3,805+72%0.022
Google Search$2,811+30%4.70192
Google Shopping$540−59%7.8062
Bing Search$274−16%3.8118

Demand Gen is the largest line item in the account and it produced two orders. DGEN_US_NB_TSD_HairSerum_Discovery spent $3,805 in 30 days at ROAS CDP 0.02, and spend grew 72% against the prior period. It is not merely underperforming — it is scaling while underperforming. This is the first thing to address on this account.

One caveat before cutting: Demand Gen splits across two CDP source categories — Google Discovery carries the spend and Google Display carries some of the orders. Both are included above and the combined picture is still two orders. The halo argument was also tested and did not hold: break-even would require Demand Gen to be driving 57% of all brand orders.

Structure notes

  • Brand Search is Manual CPC (CPC_Core, Modifiers, Products, Reviews_Script) plus a Max Conv Value Reviews arm. SR_US_BR_SpaDr_CPC_Core holds 90.9% Top IS.
  • Non-Brand: SR_US_NB_SpaDr_Reviews (tCPA, 65.2% Top IS) and SR_US_NB_SpaDr_Serum_Group1_Advertorial (Target Spend).
  • Shopping Non-Brand was paused in April 2026; its negative-keyword automation still runs against paused campaigns.
  • PMax excludes traffic through a campaign-attached shared negative list (sharedSets/12135164772), never per-term. Always add to that same shared set.

Open problems and what I would do

  1. Cut or fix Demand Gen this month. $3,805 for two orders, growing. If it is being kept for reach, that decision needs to be written down somewhere and given a budget ceiling.
  2. Shopping fell 59% but returns ROAS CDP 7.80 — the best return in the account, shrinking. Find out why before it goes to zero.
  3. Google Search grew 30% at ROAS CDP 4.70. Healthy. This is where the Demand Gen budget most plausibly belongs.
  4. Watch for zero-conversion days. This account has a known tracking anomaly — occasional Saturdays and mid-week days with normal impression share and clicks but no conversions. Verify in GA4 or Shopify before treating one as a performance problem.

3. Operating cadence

EveryoneRows filter by owner

What ran, how often, and — the column that actually matters — what happens if it is skipped. Filter by a name above and the rows narrow to that person's work. Anything automated is covered in Part 5; this is the rhythm, including the manual work that no script does.

3.1 Daily

WhatWhy it existsTimeIf skippedOwner
Read the SHM Search Brand daily DM Core_2 USA vs yesterday and same-day-last-week. Brand is where a bidding mistake shows up within 24h5 min A CPC or IS swing goes unseen for a day. On brand that is real money Juan Paiva
Read the TSD daily DM (Google & Bing World) Per-segment spend, orders, ROAS CDP against target, with a traffic-light 5 minSame — TSD is small enough that one bad day matters Juan Paiva
Read the SHM & TK vs Antares DM Actuals vs plan by brand and country. The only daily view of pacing against target 5 minPacing drift compounds; by the time it is visible monthly it is too late to correctAll three
Check the TK negative-keyword apply ran Miners stage candidates at 04:45 and 05:00; applies run 08:30 and 13:30. Both target campaigns that are currently paused2 min Nothing today — the campaigns are paused. It matters only if one is reactivated; see Part 5.1Juan Alexander
Glance at promo drift notifications Desktop alert only when drift changes. Silence is the healthy state 0 minThis is the seven-day-wrong-landing-page failure. The watchdog dies with the MacUnassigned

3.2 Weekly — Monday is the heavy day

WhatWhy it existsHow longIf skippedOwner
WoW slide deck — 14 segments: SHM Search Brand ×4 countries ×3 variants, SHM Bing ×4, TK Google, TK Bing The weekly business review. Narrative, not a KPI dump: every significant change gets a root cause~half a day The review happens without numbers. This is the most visible thing on the list Unassigned — splits 3 ways
WoW Google Sheets companion — 10 tabs The deck's backing detail, per channel and country~30 min, scripted Deck still ships; drill-down questions have no answerSame as the deck
TSD Monday WoW report (HTML) Fully automated in n8n now — publishes and DMs the linkread only Runs itself, until the credential diesJuan Paiva
TK Non-Brand keyword expansion Promote converting search terms into keywords. The miner removes waste; this adds reach~45 min Non-brand stops growing. Slow, invisible decayJuan Alexander
Microsoft Ads health pass MSAN off on every Search campaign is the first check, every time. Then IS and rank-lost by campaign~30 min MSAN silently re-enables budget into the Audience Network at poor ROAS Cassia

3.3 Every two weeks

WhatHow longIf skippedOwner
Promo cycle — sitelinks, promotion extensions and callouts across 6 accounts. Full detail in Part 4 2–4 h including the audit Ads point at an expired or wrong collection. Customer-visible, and the documented failure has happened twiceUnassigned

3.4 On demand

WhatWhen you'd do itOwner
SHM brand bid proposal, split by country When Top IS drifts off the 88–90% band. Read-only: proposes into a Sheet, never appliesJuan Paiva
TSD PMax / Demand Gen negative mining When a fresh search-terms export lands. PMax excludes via a campaign-attached shared list, never per-termJuan Paiva
Merchant Center feed audit On a disapproval spike. Note the status rollup lies by countryPaiva / Alexander
RSA copy refresh When ad strength drops or a promo needs new headlines. Brand RSAs stay category-neutralPaiva / Alexander
Log every platform change to the Notion changelog Always, for Trafilea accounts. Bids, budgets, pauses, negatives, ad copy, new campaigns, assets. Not for analysis or reportsAll three

4. The promo cycle

EveryoneHighest risk

Every ~2 weeks a new promotion goes live and sitelinks, promotion extensions and callouts have to change across six accounts. It is the highest-frequency manual process in this handover and the one with the worst failure record. It also has no owner after Friday.

One script writes to all six accounts in a single run. scripts/update_promo_extensions.py covers SHM Microsoft Ads (4 markets), TK Microsoft Ads, SHM Google (US/AU/UK plus the separate Canada account) and TK Google. Under the new split that means Cassia running it would mutate Juan Paiva's and Juan Alexander's Google accounts. Running it per-account with --account is possible, but the detection keyword lists are global — and once three people maintain them separately they drift. That drift is the documented cause of "EOFY Sale" serving for three cycles.

4.1 The rhythm

  1. Marketing announces the promo in a dedicated Slack channel, #mktcampaign_<brand>_<promo>_sale. Channels are revived, not recreated, so an old channel suddenly waking up is the signal.
  2. Slack is the only source for the promo dates. The website never publishes them. Read them off the go-live post.
  3. Read the live collection URLs off each market's homepage (see 4.2).
  4. Edit the config block at the top of the script: name, start date, end date, per-market URLs, discount, and the detection lists.
  5. Dry run. Read the diff line by line.
  6. --push, then log to the Notion changelog.
  7. Run the post-push audit. Not optional — see 4.4.

4.2 Read the URLs off the live site every cycle

Never reuse last cycle's slug. Each market homepage embeds a "saleUrl" naming the collection the site is currently pushing — public, no auth, no VPN:

curl -s https://shapermint.com/ | grep -o '"saleUrl":"[^"]*"'
…and the same for /en-CA/, /en-GB/, /en-AU/

Then validate each candidate: the <title> must name the promo and the page must carry a full product grid. An empty collection still returns HTTP 200.

The suffix convention is not a convention. It changes between cycles and between brands in the same cycle. August 2026: UK was -gb, not the -uk of July. Canada's Labour Day was /labour-day-sale with no country suffix, while -ca returned 200 and was empty. In the current Warehouse Clearance cycle TK Canada flips back to the bare slug while SHM Canada keeps -ca. Verify all six every time.

4.3 The current cycle: Warehouse Clearance, and why it needs two pushes

Verified live 1 September 2026. Discounts: SHM 70%, TK 60%.

MarketCollectionRuns
SHM UK & EUR/en-GB/collections/warehouse-sale-uk 3–24 Sep
SHM AU / NZ / SG/en-AU/collections/warehouse-sale-au 3–24 Sep
SHM US/collections/warehouse-sale10–24 Sep
SHM CA/en-CA/collections/warehouse-sale-ca10–24 Sep
TK US/collections/warehouse-sale10–24 Sep
TK CA/en-CA/collections/warehouse-sale — bare slug this cycle10–24 Sep

The script supports end_date per market through market_overrides but not start_date — it always reads the global PROMO["start_date"]. So a staggered cycle needs two pushes: one around 2 Sep for UK+AU with US/CA held on Labor Day via overrides, then a second around 9 Sep to flip the base and drop the overrides. Patching the script to take a per-market start date would remove this whole class of work permanently, and is the single highest-value fix in this document.

4.4 The detection-list trap

Two of the three keyword lists are not URL-guarded, so any keyword that matches the current cycle's own text makes the script re-flag and re-create the assets it just made, on every run.

  • OLD_PROMO_URL_KEYWORDS — URL-guarded. The current cycle's slugs can safely live here.
  • OLD_PROMO_TEXT_KEYWORDSnot guarded. Rotate every cycle.
  • OLD_CALLOUT_KEYWORDSnever guarded, because callouts carry no URL. This list is the only detection path for them.

For this cycle: remove "warehouse sale" from both text lists before pushing, and add "labor day" / "labour day" back as those markets leave Labor Day. Keep "/warehouse-sale" in the URL list — that one is guarded. Ended promo names also belong in STALE_TO_PAUSE in apply_brand_promo_headlines.py; the two lists have drifted apart before.

4.5 The post-push audit is mandatory

The push log is blind to what the script never detected. Pull enabled campaign_asset and customer_asset by API and assert three things: zero prior-promo names still enabled, zero cross-country URL leaks, and every sitelink and callout carrying a non-empty end date.

python3 scripts/handover_pull.py promo-assets then read reports/handover/data/MITIGATION.md

An asset with an empty end date serves forever. On Google, empty start_date/end_date means unlimited delivery. On Microsoft Ads the trap is sharper: PromotionEndDate is the redemption window and does not stop delivery — only Scheduling.EndDate does. SHM Microsoft Ads currently holds a promotion extension whose PromotionEndDate is 2018 and which is still Active. This is exactly how "EOFY Sale" (AU) and "Canada Day Sale" (CA) served from June into August 2026, across three cycles, undetected.

4.6 The coverage gap

The updater only swaps assets that already carry a previous promo. A campaign that never had a promo sitelink or callout is skipped forever. Campaign-level assets override account-level ones per field type, so a campaign with its own evergreen sitelinks but no promo sitelink inherits nothing from the account.

Truekind had no promo callout anywhere until August 2026. Fill gaps with scripts/tk_promo_coverage_fill.py (dry-run by default, idempotent, reuses the live promo sitelink rather than duplicating it) immediately after the main push. Known open gap: SHM account 1037305439 has no account-level promo fallback, so its Shopping US/AU and Demand Gen campaigns receive nothing.

4.7 The safety net, and that it dies on Friday

promo_drift_watchdog.py runs every two hours during working hours and answers one question: do the landing pages our ads point at still match the promo the website is actually running? It is read-only, notifies only when the drift changes, and it exists because SHM UK moved to the Summer Bank Holiday Sale on 13 August 2026 and paid search found out on the 20th — seven days of UK brand ads pointing at the wrong collection.

It runs on the Mac. After Friday there is no watchdog. Whoever owns the promo cycle should either migrate it to n8n or add a manual mid-cycle check to their calendar.

5. Automations and continuity

Everyone

Twenty Trafilea jobs are scheduled on Carlos's Mac; nineteen are loaded. Four workflows run server-side in n8n. Every job list below was read from launchctl and the n8n API on 1 September 2026 — not from the workspace documentation, which has been wrong about job state before.

5.1 The four that write to Google Ads

These are not reports — they mutate ad accounts through the API from a laptop that is being returned. Only one of them touches campaigns that are actually serving. Verified against the API on 1 September 2026: the three negative-keyword jobs all write into campaigns that are paused. Decide for each one: migrate it, or switch it off deliberately — letting them stop on their own is how an automation is forgotten.

JobTimeWhat it writesTarget stateOwner
tsd-nb-apply05:30 Negatives into SHP_TSD_US_NB_Haircare, _SkinCare, _BodyCare All three paused — no delivery impactJuan Paiva
shp-tk-nb-apply08:30 Negatives into SHP_US_NB_TK_ALL_STORE_PURCHASE Paused — no delivery impactJuan Alexander
brand-promo-auto-off11:00 --push: pauses brand promo headlines once their promo has ended. SHM and TK Live, serving campaignsPaiva + Alexander
tk-nb-apply13:30 Negatives into SR_US_TK_NB_BraFit, _WirelessBras, _SpecialtyBras All three paused — no delivery impactJuan Alexander

The one that matters is brand-promo-auto-off. It is the only job here writing to campaigns that serve, and it has the worst failure mode in this document: if it stops, brand promo headlines keep running after the promo has ended, which customers see. It is also the only one of the four that requires an explicit --push; the three negative jobs mutate unconditionally and have no dry-run mode.

The three negative jobs cost nothing when they stop. The risk they carry is different and slower: if anyone reactivates those paused campaigns, the miner that used to keep them clean is gone, and nothing will say so. Whoever turns one back on has to restart the miner or accept the waste.

Each miner stages a candidate file that its apply job consumes: tsd-nb-miner at 04:30, tk-nb-miner at 04:45, shp-tk-nb-miner at 05:00. Miners are read-only. Every applied batch is written to reports/<brand>-negatives/…-applied-YYYY-MM-DD.json, which is the rollback record: find the negatives there and remove them in the Google Ads UI.

5.2 Runs server-side in n8n — survives the Mac, not the credentials

These four keep running after Friday. They also keep authenticating as Carlos. When his Google and Trafilea accounts are deactivated, the Google Ads credential fails with invalid_grant and the Antares cookie stops refreshing. The workflows will not stop — they will run and fail, or run and publish a stale plan.

WorkflowScheduleDepends on
TSD Daily Slack Report — Google & Bing World04:15 COT Google Ads OAuth credential (Carlos's refresh token), Redash, Slack
TSD WoW HTML — WeeklyMon 04:00 COT Same, plus a private GitHub publishing repository and Cloudflare Pages, both on an account outside Trafilea's control
SHM&TK vs Antares — Daily04:20 COT Redash, the cached Antares plan, Slack. Consumed by the wider team
SHM&TK Antares Plan — Refresh03:50 COT Antares session cookie harvested from Carlos's browser profile

Rotating the Google Ads credential without a human re-consent is possible today via scripts/n8n_gads_cred_rotate.py — but it injects the Mac's refresh token, so it stops being a fix the moment the Mac is gone. The durable answer is to re-mint both credentials under a Trafilea-owned service identity before Friday.

5.3 Reports and trackers that stop on Friday

JobWhat it producesWhenOwner
shm-brand-dailySlack DM: SHM Search Brand Core_2 USA vs yesterday and same-day-last-week04:00Juan Paiva
shm-brand-is-trackerSHM brand impression-share recovery tracker + Sheet06:00Juan Paiva
shm-au-brand-is-trackerDaily tracking sheet for the AU Target‑IS vs Manual CPC test04:35Juan Paiva
shm-braproducts-is-trackerDaily sheet for the Bra_Products Target‑IS test04:30Juan Paiva
tsd-daily-slackRedundancy layer for the n8n TSD daily. Kept loaded on purpose — on 11 Aug n8n and the Mac both failed and the catchup retry was the only reason the report shipped04:15Juan Paiva
tsd-youtube-dashboardPublishes the TSD YouTube impact dashboard06:00Juan Paiva
promo-drift-watchdogCompares live site promo collections against enabled promo assets. The safety net for the whole promo cycleevery 2h, working hoursUnassigned
tk-nb-miner / shp-tk-nb-miner / tsd-nb-miner Stage negative-keyword candidates for the apply jobs04:45 / 05:00 Alexander / Paiva
wow-bing-proposal-guardGuard for the Bing WoW proposal stepweeklyCassia
daily-reports-catchupInfrastructure: retries any daily report that failed, on every wake between 07:00 and 23:00. This is why VPN outages have been survivableon wake

One discrepancy worth checking. shmtk-antares-report is loaded on the Mac, even though the workspace documentation states it was unloaded when the job moved to n8n. Either it is duplicating the n8n report, or the documentation is stale. Verify before assuming which.

5.4 Tied to personal accounts — Trafilea cannot administer these

SurfaceDetailImpact
Cloudflare PagesA personal account outside Trafilea's control hosts tsd-wow-tribe, tsd-youtube-impact, trafilea-ppc and shm-usa-roas-drivers Every published report URL depends on an account Trafilea cannot administer
GitHub publishing repo A private repository on a personal account; n8n PUTs the built HTML there and an Action deploys it The TSD WoW publishing pipeline runs through a personal repo
Slack DM routingSLACK_CARLOS_USER_ID is the destination of every daily report Reports keep generating and land in a deactivated user's DMs

The cheapest fix for the third row is to repoint the reports at a channel instead of a person. The first two require either transferring the projects or re-hosting them under Trafilea infrastructure.

5.5 Out of scope

The same machine runs scheduled jobs that have nothing to do with Trafilea. They are not listed here and nothing in this handover depends on them.

6. Data rules

Everyone

Every rule here exists because someone got it wrong first. Each one is stated with the incident that produced it, because a rule without its reason gets dropped the first time it is inconvenient.

6.1 The CDP is the only source for business metrics

Revenue, orders, ROAS, CPA and AOV come from Redash query 2635. Never from metrics.conversions_value or metrics.conversions. Platform attribution undercounts and disagrees with what the team sees in Tableau. This has been corrected more than five times.

MetricSource
Revenuerevenue_1dc
Ordersconversions_1dc
ROASrevenue_1dc / spend
CPAspend / conversions_1dc
AOVrevenue_1dc / conversions_1dc
Funnelusers_1d, add_to_cart_1d, initiate_checkout_1d
Impression SharePlatform API only — not in the CDP
Quality ScoreGoogle Ads API only
Bids and budgetsPlatform APIs only

Say "ROAS CDP", never a bare "ROAS". The two numbers differ and readers need to know which one they are looking at.

6.2 source_category must be a list

The parameter became multi-value on 12 May 2026. Passing a bare string returns zero rows silently and any script polling for results simply times out.

Correct — {'source_category': ['Google Search']}
Wrong — {'source_category': 'Google Search'}

The shared helper wow_lib.redash_fetch coerces a string into a list as a safety net. Any new script hitting 2635 directly must use the list form.

6.3 Join on campaign_id, never campaign_name

On a single day the AU brand test carried three spellings of the same campaign in the CDP: the live name, the pre-rename form still collecting revenue, and one with a stray space. Matching on name understated the control arm's revenue by 40%. Substring matching is worse: the control's name is a literal prefix of the experiment's, so in silently merges both arms of a test into one number. Query 2635 exposes campaign_id. Use it.

Rows also split by ad, adset and strategy (Acquisition and Retention). Sum them all.

6.4 Funnel definitions match Tableau, not intuition

  • ATC% = add-to-carts ÷ users
  • IC% = initiate-checkouts ÷ add-to-carts
  • Checkout CVR = orders ÷ initiate-checkouts

The user column is users_1d. A bare users returns zero without erroring.

6.5 The platform-vs-CDP gap is attribution, not a bug

Platform-reported conversion value runs about +4% above CDP overall, roughly +2% on brand and +20% on non-brand. Spend reconciles almost exactly. The gap is the attribution window, and it is expected. Do not "fix" it, and do not mix the two sources inside one table.

6.6 CDP events settle late

A fresh pull of the last day or two will understate add-to-carts, checkouts and orders. Zeros in recent data usually mean lag, not a funnel collapse. Re-pull with max_age=0 before drawing a conclusion, and prefer windows that end yesterday rather than today.

6.7 Redash needs the VPN; the Antares MCP does not

Redash sits behind the Trafilea VPN, and VPN outages are the documented root cause of most stale-report incidents. The antares-performance-metrics MCP tool reads the same warehouse table over the public internet and was verified byte-identical to query 2635 on one brand-channel-week. It is a viable escape hatch — but re-verify equality per brand before moving any production job onto it.

6.8 Google Ads API notes that bite

  • Pin REST callers to a supported version. The Python SDK negotiates its own; raw REST (all of n8n) does not. v21 was blocked progressively in August 2026 — the same query alternated success and 400 for hours, which reads like a flaky network unless you unpack the error body. A 400 is not an auth problem; a dead token returns 401.
  • campaign.start_date does not exist in v23 — it is campaign.start_date_time.
  • There is no flat campaign.target_roas. The real paths are campaign.maximize_conversion_value.target_roas and campaign.target_roas.target_roas.
  • Impression-share metrics need campaign.advertising_channel_type = 'SEARCH' in the WHERE clause or they come back wrong on mixed-type accounts.
  • Validate any new GAQL before running it: python3 scripts/lib/gads_fields.py check <resource> <fields>. campaign.name, for instance, is not selectable from campaign_asset.

6.9 Microsoft Ads read paths that return zero instead of erroring

Three calls in the extension API look like "this account is empty" when they are actually being read wrong. All three were hit while building this document:

  • GetAdExtensionIdsByAccountId returns ArrayOflong — read .long, not AdExtensionIdentity.
  • GetAdExtensionsByIds returns .AdExtensions, plural.
  • GetAdExtensionsAssociations takes campaign ids as EntityIds plus AdExtensionType — passing AdExtensionIds raises a type error and omitting the type raises a bare SOAP fault. Never wrap it in a silent except.

And the one that matters operationally: Scheduling.EndDate stops delivery; PromotionEndDate does not.

Appendix — where everything lives

Everyone

The workspace

Everything runs out of a local ppc-workspace directory, mirrored to a private GitHub repository. CLAUDE.md at the root is the operating manual: account registry, API patterns, and a section per automation with its schedule, log path and rollback procedure. It is long, it is current, and it is the first thing to read after this page. Treat its claims about job state with suspicion — it has been stale on that before.

Scripts by function

AreaEntry point
Promo cycleupdate_promo_extensions.py · tk_promo_coverage_fill.py · promo_drift_watchdog.py · auto_off_brand_promo_headlines.py
Negative keywordstk_nb_search_term_miner.py / tk_nb_apply_negatives.py · shp_tk_nb_* · shp_tsd_nb_* · tsd_pmax_nb_apply_negatives.py
Weekly WoWwow_lib.py (shared classifiers and CDP fetch) · wow_sheets_generator.py · build_tsd_wow_report.py
Daily reportsshm_brand_daily_report.py · tsd_daily_slack_report.py · shmtk_antares_report.py
Trackersshm_au_brand_is_tracker.py · shm_braproducts_is_tracker.py · shm_brand_is_recovery_tracker.py
Bingshm_bing_platform_pull.py · bing_daily_perf_export.py · bing_get_refresh_token.py
Shared librariesscripts/lib/config.py (account registries), redash.py, google_ads.py, gads_fields.py (GAQL validator), slack.py, notion.py
This documenthandover_pull.py (all data pulls) · build_handover.py

Registries worth knowing

  • reports/links-registry.md — every published report URL.
  • reports/wow-reports-registry.md — every WoW deck by week.
  • client-context/trafilea/*.md — per-brand running context. The Spa Dr's file is the longest at ~160KB and carries most of the account's history.
  • reference/gads-api/ — 1,412 annotated Google Ads fields; query it with gads_fields.py.

The Notion changelog

Every real platform change on a Trafilea account gets logged: bid changes, pauses and enables, budget changes, negative keywords, ad copy, new campaigns, asset additions or removals. Not logged: analysis, reports, audits, or anything touching the workspace itself. Database "Carlos Carrero - Changelog", id 1b5e19cdd4c580d492ddf9ac7a392567, posted through the REST API with NOTION_CHANGELOG_TOKEN — the Notion MCP does not have access to it.

This document

Source lives in reports/handover/: content fragments in parts/, styling and the owner filter in shell.html. Rebuild with python3 scripts/build_handover.py. The data behind every figure is in reports/handover/data/, regenerable with python3 scripts/handover_pull.py {bing|is|cdp|promo-assets|creds}.

The published URL sits on a personal Cloudflare account. The single HTML file is self-contained and opens with no server — keep a copy in a Trafilea Drive folder so the content survives the link.