Skip to content

Commit 49f23cd

Browse files
AgriciDanielclaude
andcommitted
feat(v1.3.0): GAQL accuracy fixes, validation gates, marketplace support
- Upstream 10 Google Ads audit accuracy fixes from community fork (akarls-web), eliminating ~600 false positives across G03, G04, G12, G16, G17, G19, G48, G-CT1 - Cherry-pick 6 validation gates from PR #12 (Tessl) for data quality enforcement - Add Google Ads MCP integration for optional automated data collection - Create marketplace.json for plugin system discoverability (Issue #14) - Add GAQL compatibility reference (gaql-notes.md) with deduplication patterns and legacy BMM detection heuristic - Update Dependabot deps: actions/checkout v6, setup-python v6, Pillow <13.0.0 - Align version to 1.3.0 (was incorrectly 2.0.0 in plugin.json) PR triage: Closed #4, #5, #13 (out of scope); cherry-picked #12; merged #8, #9, #10 Issues: Closed #14 (marketplace.json shipped) Community: Replied to Discussion #11 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent bf77f61 commit 49f23cd

13 files changed

Lines changed: 194 additions & 24 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "claude-ads",
3+
"version": "1.3.0",
4+
"description": "Comprehensive paid advertising audit, optimization, and AI creative generation skill for Claude Code. 225+ checks across Google, Meta, YouTube, LinkedIn, TikTok, Microsoft, and Apple Search Ads with weighted scoring, parallel agents, and industry templates.",
5+
"author": "AgriciDaniel",
6+
"repository": "https://github.com/AgriciDaniel/claude-ads",
7+
"license": "MIT",
8+
"install": "curl -fsSL https://raw.githubusercontent.com/AgriciDaniel/claude-ads/main/install.sh | bash",
9+
"uninstall": "curl -fsSL https://raw.githubusercontent.com/AgriciDaniel/claude-ads/main/uninstall.sh | bash",
10+
"keywords": [
11+
"advertising-audit",
12+
"google-ads",
13+
"meta-ads",
14+
"linkedin-ads",
15+
"tiktok-ads",
16+
"ppc",
17+
"paid-advertising",
18+
"claude-code-skill"
19+
]
20+
}

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "claude-ads",
3-
"version": "2.0.0",
3+
"version": "1.3.0",
44
"description": "Comprehensive paid advertising audit, optimization, and AI creative generation skill for Claude Code. 225+ checks across Google, Meta, YouTube, LinkedIn, TikTok, Microsoft, and Apple Search Ads with weighted scoring, parallel agents, and industry templates. AI-powered brand DNA extraction and ad image generation.",
55
"author": "AgriciDaniel",
66
"license": "MIT",

CHANGELOG.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,48 @@ All notable changes to claude-ads are documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.3.0] - 2026-04-01
9+
10+
### Added
11+
- **marketplace.json** for plugin system discoverability and update mechanism (Issue #14)
12+
- **Validation gates** in 6 skills — cherry-picked from PR #12 (Tessl):
13+
- `ads/SKILL.md`: Task tool orchestration clarity + subagent JSON score verification
14+
- `ads-audit`: Platform data availability check + subagent score field verification
15+
- `ads-budget`: 14-day minimum for kill/scale decisions + 20-click/$100 data sufficiency
16+
- `ads-creative`: Data existence check + assumption prevention gate
17+
- `ads-google`: 30-day data minimum + 74-check completeness verification
18+
- `ads-youtube`: Active campaign check + campaign type completeness gate
19+
- **GAQL compatibility reference** (`gaql-notes.md`): known field incompatibilities, deduplication patterns, filter scope best practices, legacy BMM detection heuristic
20+
- **Google Ads MCP integration** in ads-google: optional automated data collection via [google-ads-mcp](https://github.com/googleads/google-ads-mcp) with fallback to manual export
21+
- **Shared negative keyword list support** (G14/G15): campaigns covered by shared lists no longer flagged as "missing negatives"
22+
- **Keyword-level brand detection** (G05/G07/G-PM3): derives brand tokens from account name, classifies by keyword composition instead of campaign naming conventions
23+
- **G-SYS1 diagnostic**: guidance for reporting API fetch failures instead of silently skipping checks
24+
- **`dependencies` label** created for Dependabot PR automation
25+
26+
### Fixed
27+
- **G03**: False positives from zero-impression keywords, paused ad groups, match type duplication, and stopword-only keywords diluting coherence scores (~18% false positive reduction)
28+
- **G04**: False positives from multi-location campaign structures — now strips geographic identifiers before counting objectives
29+
- **G12**: Inverted Search Partners logic — flag OFF as missed opportunity (was incorrectly flagging ON)
30+
- **G16/G-WS1**: Wasted spend threshold raised to >$10 spend + 0 conversions (was flagging all non-converting terms including long-tail exploration)
31+
- **G17/FL04**: Legacy BMM false positives — BROAD + Manual CPC is legacy BMM (not intentional broad). Only flags BROAD in Smart Bidding campaigns
32+
- **G19**: Search term visibility calculated from ALL fetched terms before truncation (was computing from truncated subset)
33+
- **G48/CT-FL5**: False flags on Smart Campaign system-managed conversions excluded from DDA and counting-type checks
34+
- **G-CT1**: False duplicate detection on HIDDEN/REMOVED conversion actions — now only checks ENABLED actions
35+
- **Conversion tracking**: Added duplicate detection accuracy rules (exclude HIDDEN/REMOVED, exclude Smart Campaign system conversions)
36+
37+
### Changed
38+
- Dependabot: actions/checkout v4 → v6, actions/setup-python v5 → v6, Pillow `<12.0.0``<13.0.0`
39+
- Version aligned to 1.3.0 (plugin.json was incorrectly at 2.0.0)
40+
- Reference file count: 20 → 21 (added gaql-notes.md)
41+
42+
### Community
43+
- Closed PRs #4, #5, #13 (out of scope: white-label rebrand, campaign system, FastAPI web app)
44+
- Cherry-picked validation improvements from PR #12 (Tessl) — 6 of 18 files
45+
- Replied to Discussion #11 ("Does this really work?")
46+
- Closed Issue #14 (marketplace.json shipped)
47+
- GAQL accuracy fixes sourced from akarls-web fork (44 commits of audit engine improvements)
48+
- MCP integration sourced from double-agency fork
49+
850
## [1.2.0] - 2026-03-12
951

1052
### Added

ads/SKILL.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,10 @@ When the user invokes `/ads audit`, delegate to subagents in parallel:
6161
1. **Collect context** (see Context Intake above — do this first)
6262
2. Collect account data (exports, screenshots, or pasted metrics)
6363
3. Detect business type and identify active platforms
64-
4. Spawn subagents: audit-google, audit-meta, audit-creative, audit-tracking, audit-budget, audit-compliance
65-
5. Collect results and generate unified report with Ads Health Score (0-100)
66-
6. Create prioritized action plan with Quick Wins
64+
4. Spawn subagents via Task tool with `context: fork`: audit-google, audit-meta, audit-creative, audit-tracking, audit-budget, audit-compliance
65+
5. **Validate**: verify each subagent returned valid JSON scores with required fields before aggregating
66+
6. Collect results and generate unified report with Ads Health Score (0-100)
67+
7. Create prioritized action plan with Quick Wins
6768

6869
For individual commands (`/ads google`, `/ads meta`, etc.), load the relevant
6970
sub-skill directly. Still collect context first if not already provided.

ads/references/conversion-tracking.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ gtag('consent', 'update', {
6464
- Separate micro (AddToCart, TimeOnSite) from macro (Purchase, Lead) conversions
6565
- Only macro conversions as "Primary" for bidding optimization
6666

67+
### Duplicate Detection Accuracy
68+
- Only check **ENABLED** conversion actions for duplicates — exclude HIDDEN and REMOVED actions (they cannot cause double-counting)
69+
- Exclude Smart Campaign system-managed conversions (e.g., 'Smart campaign map clicks to call') from DDA and counting-type checks — their attribution model and counting type are locked by Google and cannot be changed by advertisers
70+
6771
---
6872

6973
## Meta Ads Conversion Tracking

ads/references/gaql-notes.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# GAQL Compatibility & Accuracy Notes
2+
3+
<!-- Added: 2026-04-01 -->
4+
<!-- Source: akarls-web fork audit accuracy improvements (44 commits) -->
5+
<!-- Purpose: Prevent false positives when analyzing Google Ads data via GAQL or exports -->
6+
7+
## Known GAQL Field Incompatibilities (API v20+)
8+
9+
| Resource | Incompatible Field | Error | Fix |
10+
|----------|-------------------|-------|-----|
11+
| `search_term_view` | `campaign.status`, `ad_group.status` | INVALID_ARGUMENT | Filter status in application layer, not GAQL |
12+
| `search_term_view` | `search_term_view.status` | INVALID_ARGUMENT | Field deprecated/removed in v20 |
13+
| `asset_group_signal` | `audience_signal` | UNRECOGNIZED_FIELD | Use `resource_name` instead |
14+
| DURING clause | `LAST_90_DAYS` | INVALID_VALUE_WITH_DURING_OPERATOR | Use `LAST_30_DAYS` for search terms |
15+
16+
## Keyword Deduplication
17+
18+
**Problem:** `keyword_view + segments.date DURING LAST_30_DAYS` returns one row per keyword per day. A keyword active 5 days = 5 rows. Same keyword with BROAD + PHRASE = 2 rows per day = 10 total.
19+
20+
**Fix:** Deduplicate by `(ad_group_id + keyword_text + match_type)` at fetch time. Aggregate metrics (impressions, clicks, cost, conversions) across duplicate rows.
21+
22+
**Alternative:** Remove `segments.date` from GAQL queries entirely to eliminate date-level duplication at source.
23+
24+
**Impact:** All downstream keyword-dependent checks (G03, G05, G07, G-PM3, G17, G21, G25, G-KW1, and ~10 others) automatically use correct unique counts.
25+
26+
## Filter Scope Best Practices
27+
28+
For active audits, filter to ENABLED resources only:
29+
- **Campaigns:** `campaign.status = 'ENABLED'` (not `!= 'REMOVED'`, which includes PAUSED)
30+
- **Ad groups:** ENABLED campaigns + non-removed groups
31+
- **Keywords:** ENABLED campaigns + non-removed groups + non-removed keywords
32+
- **Search terms:** Extended from `LAST_30_DAYS` to `LAST_90_DAYS` for deeper analysis, ordered by cost DESC
33+
34+
**Why:** Including paused campaigns/ad groups causes false positives. Paused ad groups can have ENABLED keywords at criterion level but aren't visible in the UI — auditing them confuses users.
35+
36+
## Error Handling
37+
38+
Track which data fetches failed and why. Report as a G-SYS1 diagnostic:
39+
- List all failed data sources with error messages
40+
- Provide per-check context on which checks were skipped due to missing data
41+
- Never silently skip checks — always explain why data is unavailable
42+
43+
## Legacy BMM (Broad Match Modified) Detection
44+
45+
Google stripped '+' prefixes during the 2021 migration but kept `matchType='BROAD'` in the API.
46+
47+
**Heuristic:** True intentional broad match is ALWAYS paired with Smart Bidding (tCPA, tROAS, Maximize Conversions/Value). BROAD + Manual CPC = legacy BMM (behaves as phrase match).
48+
49+
**Impact:** Without this heuristic, accounts with legacy BMM keywords generate hundreds of false failures on G17.

ads/references/google-audit.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,12 @@
2929
| G46 | Conversion window appropriate | Medium | Window matches sales cycle (7d ecom, 30-90d B2B, 30d lead gen) | Default 30d without validation | Window mismatched to sales cycle |
3030
| G47 | Micro vs macro separation | High | Only macro conversions (Purchase, Lead) set as "Primary" for bidding | Some micro events as Primary | All events including micro (AddToCart, TimeOnSite) as Primary |
3131
| G48 | Attribution model | Medium | Data-driven attribution (DDA) selected | Last Click (intentional) | Rule-based model still active (deprecated Sep 2025) |
32+
33+
**G48/CT-FL5 accuracy notes:** Exclude Smart Campaign system-managed conversions (e.g., 'Smart campaign map clicks to call') from DDA and counting-type checks. Their attribution model and counting type are locked by Google — advertisers cannot change them. Only evaluate advertiser-controlled conversion actions.
3234
| G49 | Conversion value assignment | High | Dynamic values for ecom; value rules for lead gen | Static values assigned | No conversion values |
3335
| G-CT1 | No duplicate counting | Critical | GA4 + Google Ads not double-counting same conversion || Both GA4 import and native tag counting same action |
36+
37+
**G-CT1 accuracy notes:** Only check ENABLED conversion actions for duplicates. Exclude HIDDEN and REMOVED conversion actions — these are already disabled and cannot cause double-counting. When reporting duplicates, include the conversion action ID, type, origin, category, status, primary/secondary flag, counting type, and attribution model for easy resolution.
3438
| G-CT2 | GA4 linked and flowing | High | GA4 property linked, data flowing correctly | Linked but data discrepancies | Not linked |
3539
| G-CT3 | Google Tag firing | Critical | gtag.js or GTM firing correctly on all pages | Firing on most pages (>90%) | Tag missing or broken on key pages |
3640

@@ -43,10 +47,18 @@
4347
| G13 | Search term audit recency | Critical | Search terms reviewed within last 14 days | Reviewed within 30 days | Not reviewed in >30 days |
4448
| G14 | Negative keyword lists exist | Critical | ≥3 theme-based lists (Competitor, Jobs, Free, Irrelevant) | 1-2 lists exist | No negative keyword lists |
4549
| G15 | Account-level negatives applied | High | Negative lists applied at account or all-campaign level | Applied to some campaigns only | Not applied |
50+
51+
**G14/G15 accuracy notes:** Count both campaign-level negatives AND Shared Negative Keyword Lists when evaluating coverage. Campaigns covered by shared lists should NOT be flagged as "missing negatives." Report per-campaign breakdown showing direct negatives vs. shared list assignments for clear remediation paths.
4652
| G16 | Wasted spend on irrelevant terms | Critical | <5% of spend on irrelevant search terms (last 30d) | 5-15% on irrelevant terms | >15% on irrelevant terms |
53+
54+
**G16/G-WS1 accuracy notes:** Only flag search terms as "wasted" if they have >$10 spend AND 0 conversions. Long-tail terms with minimal spend (<$10) are normal exploration, not waste. When reporting, show top 10 wasters with spend and click details.
4755
| G17 | Broad match + smart bidding pairing | Critical | No Broad Match keywords running on Manual CPC || Broad Match + Manual CPC active |
56+
57+
**G17/FL04 legacy BMM heuristic:** Google stripped '+' prefixes from Broad Match Modified keywords during the 2021 migration but kept `matchType=BROAD` in the API. BROAD + Manual CPC almost always indicates legacy BMM (behaves as phrase match), NOT intentional broad match. True intentional broad match is always paired with Smart Bidding (tCPA, tROAS, Maximize Conversions/Value). Only flag BROAD keywords in Smart Bidding campaigns as needing review. Skip BROAD + Manual CPC — these are legacy BMM and should not be flagged as failures.
4858
| G18 | Close variant pollution | High | Exact/Phrase match not triggering irrelevant close variants | Minor close variant issues | Significant irrelevant close variant spend |
4959
| G19 | Search term visibility | Medium | >60% of search term spend is visible (not hidden) | 40-60% visible | <40% visible |
60+
61+
**G19 accuracy notes:** When computing `totalVisibleSpend`, use ALL fetched search terms before any truncation or top-N limiting. A common error is summing cost from a truncated subset (e.g., top 500 of 2000 terms) which understates visibility. Fetch terms ordered by cost descending to ensure the highest-spend terms are captured first.
5062
| G-WS1 | Zero-conversion keywords | High | No keywords with >100 clicks and 0 conversions | 1-3 such keywords | >3 keywords with >100 clicks, 0 conversions |
5163

5264
---
@@ -58,15 +70,23 @@
5870
| G01 | Campaign naming convention | Medium | Consistent pattern (e.g., [Brand]_[Type]_[Geo]_[Target]) | Partially consistent | No naming convention |
5971
| G02 | Ad group naming convention | Medium | Matches campaign naming pattern | Partially consistent | No naming convention |
6072
| G03 | Single theme ad groups | High | Each ad group targets 1 keyword theme (≤10 keywords) | 11-20 keywords with consistent theme | Ad groups with 20+ unrelated keywords (theme drift) |
73+
74+
**G03 accuracy notes:** When evaluating theme coherence: (1) Only count keywords with impressions > 0 — dormant zero-impression keywords don't affect ad serving and shouldn't inflate counts. (2) Exclude paused ad groups — `ENABLED` ad groups only (paused groups can have enabled keywords at criterion level but aren't visible in UI). (3) Deduplicate keywords by text per ad group — the same keyword with BROAD + PHRASE match types is one keyword, not two. (4) Exclude stopword-only keywords (e.g., 'attorney', 'lawyers') from coherence scoring — they carry no thematic signal and dilute coherence scores.
6175
| G04 | Campaign count per objective | High | ≤5 campaigns per funnel stage/objective | 6-8 campaigns per objective | >8 campaigns per objective (fragmented) |
76+
77+
**G04 accuracy notes:** For multi-location businesses, strip geographic identifiers (city names, state abbreviations, zip codes, metro areas, directional qualifiers like "North"/"South") from campaign names before counting unique objectives. A firm running "Divorce - Chicago", "Divorce - Schaumburg", "Divorce - Naperville" has 1 objective across 3 geos, not 3 separate objectives. Preserve PPC-meaningful terms (brand, nonbrand, pmax, remarketing, etc.).
6278
| G05 | Brand vs Non-Brand separation | Critical | Brand and non-brand in separate campaigns || Brand and non-brand mixed in same campaign |
79+
80+
**G05/G07/G-PM3 brand detection:** Don't rely solely on campaign naming conventions. Derive brand tokens from the account/business name and scan actual keyword text for brand terms. Classify campaigns by keyword composition: >50% brand keywords = brand campaign. This catches mislabeled campaigns and provides accurate brand vs. non-brand separation.
6381
| G06 | PMax present for eligible accounts | Medium | PMax active for accounts with conversion history | PMax tested but paused | No PMax tested despite eligibility |
6482
| G07 | Search + PMax overlap | High | Brand exclusions configured in PMax when Search brand campaign exists | Partial brand exclusions | No brand exclusions in PMax alongside brand Search |
6583
| G08 | Budget allocation matches priority | High | Top-performing campaigns not budget-limited | Minor budget constraints on top performers | Top performers severely budget-limited |
6684
| G09 | Campaign daily budget vs spend | Medium | No campaigns hitting budget cap before 6PM | 1-2 campaigns hitting cap early | Multiple campaigns capped before noon |
6785
| G10 | Ad schedule configured | Low | Ad schedule set if business has operating hours || No schedule despite clear business hours |
6886
| G11 | Geographic targeting accuracy | High | "People in" (not "People in or interested in") for local || "People in or interested in" for local business |
69-
| G12 | Network settings | High | Search Partners and Display Network disabled for Search (unless intentional) | Search Partners ON (monitored) | Display Network ON for Search campaign |
87+
| G12 | Network settings | High | Search Partners enabled for additional reach; Display Network disabled for Search (unless intentional) | Search Partners OFF (missing incremental reach) | Display Network ON for Search campaign |
88+
89+
**G12 note:** Search Partners typically provides incremental reach at comparable CPA. Flag Search Partners OFF as a missed opportunity (Warning), not ON. Display Network on Search campaigns remains a Fail.
7090

7191
---
7292

install.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ main() {
9090
echo " • 1 main skill (ads orchestrator)"
9191
echo " • 17 sub-skills (platform + functional + creative)"
9292
echo " • 10 agents (6 audit + 4 creative)"
93-
echo "20 reference files"
93+
echo "21 reference files"
9494
echo " • 11 industry templates"
9595
echo ""
9696
echo "Usage:"

skills/ads-audit/SKILL.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,19 @@ description: >
1313
## Process
1414

1515
1. **Collect account data** — request exports, screenshots, or API access
16-
2. **Detect business type** — analyze account signals per ads orchestrator
17-
3. **Identify active platforms** — determine which platforms are in use
18-
4. **Delegate to subagents** (if available, otherwise run inline sequentially):
16+
2. **Validate**: confirm at least one platform's data is available before proceeding
17+
3. **Detect business type** — analyze account signals per ads orchestrator
18+
4. **Identify active platforms** — determine which platforms are in use
19+
5. **Delegate to subagents** (if available, otherwise run inline sequentially):
1920
- `audit-google` — Conversion tracking, wasted spend, structure, keywords, ads, settings (G01-G74)
2021
- `audit-meta` — Pixel/CAPI health, creative fatigue, structure, audience (M01-M46)
2122
- `audit-creative` — LinkedIn, TikTok, Microsoft creative checks + cross-platform synthesis
2223
- `audit-tracking` — LinkedIn, TikTok, Microsoft tracking + cross-platform tracking health
2324
- `audit-budget` — LinkedIn, TikTok, Microsoft budget/bidding + cross-platform allocation
2425
- `audit-compliance` — All-platform compliance, settings, performance benchmarks
25-
5. **Score** — calculate per-platform and aggregate Ads Health Score (0-100)
26-
6. **Report** — generate prioritized action plan with Quick Wins
26+
6. **Validate**: verify each subagent returned valid scores with required fields before aggregating
27+
7. **Score** — calculate per-platform and aggregate Ads Health Score (0-100)
28+
8. **Report** — generate prioritized action plan with Quick Wins
2729

2830
## Data Collection
2931

0 commit comments

Comments
 (0)