google-ads-optimizer
Skill11 starsChanged 5 months agoMITView on GitHub
Acts on a running account to improve performance: campaign tiering, search-term mining into negatives, budget reallocation, target ROAS/CPA adjustments, bid-strategy changes, asset refresh, and consolidation. Diagnoses root cause (not just symptoms) and produces a dated action plan. Proposes changes — applies via pusher with approval. Generalized fork of a production optimizer; reads account-context.yaml, respects guardrails and margin tiers. Use when the user says "optimize", "improve ROAS", "lower CPA", "add negatives", "raise tROAS", "reallocate budget", "fix underperformers", "weekly ads review".
1 other project uses a version of this.
Adopted by
Most recently taken up by
- narayan-metaflow/metaflow-marketing-skillstheir own version, first seen Apr 17, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- narayan-metaflow/metaflow-marketing-skills★ 14same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository14 other files it gives its agents.
- google-ads-assetsSkill
- google-ads-auditSkill
- google-ads-builder-branded-searchSkill
- google-ads-builder-demand-genSkill
- google-ads-builder-pmaxSkill
- google-ads-builder-searchSkill
- google-ads-experimentsSkill
- google-adsSkill
- google-ads-measurementSkill
- google-ads-planSkill
- google-ads-pusherSkill
- google-ads-routineSkill
- google-ads-setupSkill
- google-ads-trackerSkill
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 2 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 19
Every recommendation has data backing (specific numbers, not vague advice).
- Very specific: line 20
Read everything from account-context.yaml (margin_tiers, brand_terms, guardrails, AOV).
- Very specific: line 22
Read the context
connectionsblock first: If store/GA4 is missing, do the in-platform analysis and label every true-ROAS / store-revenue … - Distinctive: line 120
Don't stop at symptoms. For each problem ask "why" until you reach the cause (e.g.
- Very specific: line 140
Every action carries a reason (the $ leak / numbers), its diagnostic D-code, est_impact_per_mo, and a push_path.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Google Ads — Optimizer (act)
Find what's dragging the account and fix it — with data backing and root-cause reasoning, not symptom
swatting. The optimizer proposes changes; it applies them only through pusher (approval gate). For a
scored health check use audit; this skill is about performance and money.
Operating rules
- Every recommendation has data backing (specific numbers, not vague advice).
- Read everything from
account-context.yaml(margin_tiers, brand_terms, guardrails, AOV). If the context is missing, runsetupfirst — never optimize a live account without it. - Read the context
connectionsblock first. If store/GA4 ismissing, do the in-platform analysis and label every true-ROAS / store-revenue conclusion UNVERIFIED — connect store/GA4; never fabricate a store-revenue figure to compute "true ROAS". Prefer guiding the user to connect over guessing. - 3-source attribution (see
${CLAUDE_PLUGIN_ROOT}/references/optimization-playbook.md): store revenue = ground truth; Google Ads in-platform = for Smart Bidding; GA4 = channel mix. A 20-35% Ads-vs-GA4 gap is normal. - Honor guardrails: change-event cooldown, ignore paused, margin-tier ROAS. Don't flag a house-brand line
for low ROAS above its tier
min_roas.
Model dispatch (run cheap, decide expensive) — see ${CLAUDE_PLUGIN_ROOT}/references/model-tier-dispatch.md
- Scout (
haiku) — runningtiering.pyandsearch_term_miner.py(scripts return their own output). - Routine (
sonnet) — STEP 1 performance pull + store/GA4 fetch; STEP 3 dual-source search-term pull (per-PMaxcampaign_search_term_insightloop). Dispatch asgeneral-purposesub-agents; return raw, don't conclude. - Judge (main session) — tier verdicts, what to block vs keep (esp. never-block-brand), STEP 5 profitability call, STEP 6 root-cause, STEP 7 dated action plan. The numbers come cheap; the decisions stay here.
STEP 1 — Collect performance
Scope to the active set first (ENABLED + impressions in the window) — never tier or "optimize" a
campaign that hasn't served in the period; entity status=ENABLED can include long-dead campaigns' assets
(see ${CLAUDE_PLUGIN_ROOT}/skills/audit/references/gaql-notes.md). Then pull campaign performance, search terms (≤30d or explicit dates),
and asset-group performance via the MCP; pull store revenue + GA4 channel mix via the data-source/GA4
fallback chain. Note what's unavailable.
STEP 2 — Tier campaigns
Classify Gold / Silver / Bronze / Dead by ROAS relative to the margin-tier target, plus conversion
volume per period. Output a ranked verdict table (Scale / Keep / Reduce / Pause / Kill) with the numbers.
Runnable: python ${CLAUDE_PLUGIN_ROOT}/skills/optimizer/scripts/tiering.py campaigns.json [--target-roas T] (margin-tier aware;
flags learning risk + pause-candidate savings).
STEP 3 — Mine search terms → negatives (BOTH sources)
Pull from both: search_term_view (Search/Branded) AND campaign_search_term_insight looped per
active PMax/Demand-Gen campaign (requires a single campaign_id filter — see gaql-notes.md; it returns
categories + conv/value, not per-term cost). search_term_view does NOT contain PMax terms, so PMax-heavy
accounts need the insight or you'll miss most of the spend. Find top spend, top revenue, wasted (Search:
$10 spend & 0 conv; PMax: irrelevant categories with clicks but no conversions). Propose Exact/Phrase negatives in themed lists; respect the "what NOT to block" rules (store / coupon / cheap-brand) in the playbook; never block brand terms (brand intent, even at 0 conv). Catch cross-brand leakage via
brand_terms.
🛑 RESELLER BRAND RULE — never negate a brand the store SELLS (the #1 false-positive)
For a distributor/reseller, a search term containing a brand you carry is buying intent, not a competitor — auto-negating it kills your own sales. Classify every brand-bearing term into THREE buckets, not two:
- OWN house brand → never block (brand defense). [
brand_terms] - CARRIED brand (a brand in your catalog/feed) — split by whether it already has its own campaign:
- has a dedicated campaign → blocking it in a catch-all (e.g. "Lite") is OK only to ROUTE traffic to
the specialist campaign (anti-cannibalization). Label it "route to
<brand>campaign", NOT "competitor". - no dedicated campaign → NEVER block. The catch-all is its ONLY home — it lives there because it hasn't been split out yet, not because it's a competitor. A 0-conv carried-brand term is a stock/PDP/feed problem OR a split-into-its-own-campaign candidate once volume justifies it. Recommend the split, never a negative.
- has a dedicated campaign → blocking it in a catch-all (e.g. "Lite") is OK only to ROUTE traffic to
the specialist campaign (anti-cannibalization). Label it "route to
- NOT carried (store genuinely doesn't sell it) → a candidate competitor negative — flag for the user to
CONFIRM it isn't carried before blocking. Never assert "competitor" on your own.
Populate the miner's brand inputs every run (don't run it blind):
carried_brands= the distinctbrandvalues from the store catalog / the Shopping feed (shopping_performance_viewproduct brand, or the D14 inventory join) ∪ anybrands_carriedinaccount-context.yaml;brands_with_own_campaign= brands that map to an existing dedicated campaign (derive from the active-campaign names, e.g. "ND pMax OPI" →opi). Without these, the miner will mislabel carried brands (Kupa/Chaun Legend/Kiara Sky on ND) as competitors — exactly the bug to avoid. Runnable:python ${CLAUDE_PLUGIN_ROOT}/skills/optimizer/scripts/search_term_miner.py terms.json(input keys:brand_terms,carried_brands,brands_with_own_campaign,min_spend,terms— each term may carryconversions/conv_value) → wasted spend + categorized, ready-to-copy wrapped negatives, with carried-brand / routing / competitor-candidate buckets kept separate and carried-brand-without-a-campaign held back from blocking.
N-gram modifiers + conflict detection (built in to the miner)
- N-gram surfacing: the miner aggregates 1- and 2-word tokens across the whole waste pool, ranked by $ and
by how many terms each appears in (≥2 = recurring). One phrase negative on a recurring junk modifier (e.g.
"how to","jobs","tutorial") kills many wasted queries at once — far more efficient than per-term negatives. Own-brand and carried-brand tokens are excluded from the surfacing (never propose cutting a brand you sell). Treat as candidates — a broad modifier can over-reach; eyeball before pushing. - Conflict detection — NEVER negate a converter: the miner cross-checks every proposed negative against the
CONVERTING queries (
conversions/conv_value> 0) and drops any that would block one (exact = same query, phrase = substring), listing them under "Conflict-blocked". Always pass terms WITH their conversion fields so this fires. At push time, also runvalidate_changeset.py --converting-terms <converters.json>(a JSON list of converting queries) so the pusher gate re-blocks any negative that would cut proven revenue — defense-in-depth alongside the never-block-brand and carried-brand gates.
STEP 4 — Asset groups / creative
Flag asset groups with ROAS below the tier target and meaningful spend, POOR ad strength (ENABLED + has
impressions — GUARD-4), and URL overlap. Recommend creative refresh via assets.
STEP 5 — Cross-reference store + GA4 (true profitability)
True ROAS = store revenue (by product/vendor) ÷ Google Ads spend. If a line's TOTAL store revenue (all channels) is below its Ads spend, it's losing money regardless of in-platform ROAS. Surface channel mix (how much is organic/direct/email) so pausing decisions account for non-paid revenue.
STEP 5.5 — Run the seven money-leak diagnostics (the core engine)
Work through D1-D14 in ${CLAUDE_PLUGIN_ROOT}/references/diagnostic-playbook.md against the active set: D1 bid/
target health (decode bidding_strategy_system_status first — budget-capped vs tROAS-too-high vs starved vs
learning; breakeven ROAS = 1/margin), D2 budget pacing & allocation (misallocation: shift $ low-ROAS→high-ROAS),
D3 geo waste, D4 dayparting, D5 search-term/spam/wrong-brand, D6 structure, D7 Quality Score, D8 PMax
channel/placement distribution (don't assume "Display burns it" — pull segments.ad_network_type, name the
real surface; most levers DON'T work on PMax), D9 ad copy/assets/extensions (missing sitelinks/callouts,
duplicate headlines, LOW assets). Each becomes a prescription with a real $ impact, a step size, and a cooldown
gate. Caveat baked in: budget-lost-IS is blind on Smart Bidding/PMax — diagnose budget constraint via
spend-vs-budget + system_status, not budget-lost-IS. For any tROAS/budget scale move, follow the Scaling
Ladder (budget first then target, never both same week, ≤+20% budget / 10–20% target steps, 14-day cooldowns,
never reverse inside the window).
STEP 6 — Diagnose root cause (System Thinking)
Don't stop at symptoms. For each problem ask "why" until you reach the cause (e.g. low ROAS → irrelevant search terms → missing negatives → no weekly review process). Check interactions: campaigns competing for the same queries, cross-brand budget leakage, over-reliance on paid vs organic. Project the cost of inaction vs the savings from fixing.
STEP 7 — Action plan
Produce a dated plan in three buckets, each item with: what to do · exact UI/Editor steps · why (data) · status checkbox.
- Today: pause Dead campaigns, cut budget on Bronze, add negative lists, fund under-invested branded.
- Tomorrow: review yesterday; raise tROAS ≤0.2-0.3x (only if ≥15 conv/week, else consolidate first); scale Gold (+budget gradually); plan consolidations.
- 2-week roadmap: dated timeline, each action typed (urgent/optimize/plan). Respect the tROAS step-up discipline and change-event cooldown throughout.
STEP 8 — Emit the change-set (the pusher hand-off)
Serialize the action plan into a change-set.json (contract: ${CLAUDE_PLUGIN_ROOT}/references/change-set.md;
template: ${CLAUDE_PLUGIN_ROOT}/templates/change-set.json) — a typed list of edits to the LIVE account
(add_negatives, exclude_geo, exclude_products, adjust_budget, adjust_target_roas, pause,
add_extensions, add_audience_signal, exclude_placements). This is distinct from a builder's
campaign-spec.json (which creates a NEW campaign).
- Every action carries a
reason(the $ leak / numbers), itsdiagnosticD-code,est_impact_per_mo, and apush_path. Express a budget reallocation as a pair (adown/pauseon the source + anadjust_budgetupon the receiver) so each leg is gated on its own. - Encode the discipline in the data, don't rely on prose: mark
target.within_cooldown: truefor any campaign with a recentchange_event(D13) or under thechange-event-cooldownguardrail, settarget.last_change_direction, and passconv_per_weekon a tROAS raise. The pusher's validator BLOCKS a scale move on a within-cooldown target — so a campaign that "shouldn't scale yet" must NOT carry a do-now scale action (defer it to the prose roadmap, or omit it). PMax scales DOWN by pausing a low-ROAS asset group, never a budget cut. - The optimizer proposes the change-set; it never writes. Hand it to
pusher(or/google-ads-push), which validates (validate_changeset.py) and renders operator actions (changeset_to_actions.py) behind the approval gate.
Applying changes
Account mutations go through pusher (approval gate + spend cap). The optimizer prepares the change-set
(STEP 8); it does not write to the account directly.
To build / refine later
- Runnable analyzers:
scripts/tiering.py+scripts/search_term_miner.py. Done. - Emit changes as a structured change-set the pusher can consume (STEP 8 →
change-set.json; validated/rendered by pusher'svalidate_changeset.py+changeset_to_actions.py). Done.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

