Install
$ agentstack add skill-fourteenwm-ppc-ai-skills-ads-checker ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Ads Checker
Purpose: Run comprehensive creative-compliance audits across your Google Ads portfolios, identify issues, track issue history across runs, detect chronic problems, and write prioritized findings to a Google Sheet.
Type: Read-only audit skill. It does not mutate Google Ads — it only reports issues and writes results to an audit Google Sheet.
What This Skill Wraps
All audit logic and all intelligence features live in one script you provide — referred to here as ads_checker_audit.py. This skill is a procedural wrapper: it determines scope, runs the script, interprets the console output, and summarizes findings. Do not reimplement the checks in the skill; the script is the single source of truth.
A small read-only companion script, read_latest_ads_checker.py, is consumed by a daily briefing to surface recent CRITICAL/HIGH findings (see "Cached-Output Contract" — a HARD constraint if you wire it into a briefing).
Both scripts are environment-specific (they hold your credentials, account registry, and Sheet ID). See the README's "Script Dependency (You Provide)" for the data contract each must satisfy.
The 10 Checks
Phase 1 (Core)
- DKI Detection — Dynamic Keyword Insertion in copy/assets (trademark risk) → HIGH
- Google AI Assets —
AUTOMATICALLY_CREATEDassets that should be disabled → MEDIUM - URL Validation — broken/invalid destination URLs (parallel HEAD checks) → CRITICAL
- Ad Disapprovals — non-approved policy statuses on ENABLED ads/ad groups/campaigns → CRITICAL (disapproved) / MEDIUM (limited)
- Seasonal Promotions — outdated seasonal language in copy/assets → HIGH
Phase 2 (Extended)
- Auto-Created Assets Setting —
TEXT_ASSET_AUTOMATIONcurrentlyOPTED_INon campaigns → HIGH - Final URL Expansion —
FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATIONOPTED_IN→ MEDIUM - Auto-Applied Recommendations — enabled recommendation subscriptions (whitelist
OPTIMIZE_AD_ROTATION; flag high-risk keyword/CPA/ROAS types) → HIGH / MEDIUM - Inappropriate Content — profanity, violence, adult, discriminatory (incl. Fair Housing), scam, competitor, spam terms → CRITICAL/HIGH by category
- Spelling/Grammar — misspellings via a spell checker, with ad-copy + brand-name exceptions → HIGH (5+) / MEDIUM (1-4)
- Irrelevance — URL-domain mismatch, template placeholders, missing brand names → HIGH/MEDIUM
> "10 checks" is the user-facing framing; the script runs 11 functions (the auto-created-assets setting is reported but not counted as a standalone issue type in the daily-briefing reader). Keep the "10 checks" framing in summaries.
Step 1 — Determine Scope
Parse the request into a script flag:
| Request | Scope | Flag | |---|---|---| | "Run ads checker" | All accounts | --portfolio all | | "…for [Portfolio]" | One portfolio segment | --portfolio | | "…for [CID]" | Single account | --cid | | "…for [Account Name]" | Look up CID, then single | --cid |
Account name → CID: resolve via your account registry (an accounts.json or equivalent — the script's single source of truth). Account counts drift; don't hardcode them.
Step 2 — Run the Audit
Run from the directory where the script's credentials file lives. Always pipe "no" to stdin so the optional chronic-issue file-creation prompt never blocks a non-interactive run (see "Chronic-Issue Handling"):
# Single account by CID
echo "no" | python ads_checker_audit.py --cid 1234567890
# A portfolio segment
echo "no" | python ads_checker_audit.py --portfolio
# All accounts
echo "no" | python ads_checker_audit.py --portfolio all
# Dry run (no Sheet write, no history comparison, no prompt)
python ads_checker_audit.py --portfolio --dry-run
Add a small delay between accounts (e.g. 0.3s) to avoid API throttling on large portfolios.
Step 3 — Intelligence Features (all in the script; preserve them)
These run automatically on any non---dry-run run. The skill surfaces them in the summary; it does not recompute them.
- Issue-history comparison — for each account, compares current counts to the most recent prior row in the history tab and tags each issue type NEW / INCREASED / DECREASED / RESOLVED / SAME. First-ever audit of an account is
FIRST_RUN. - Chronic-issue detection — for HIGH/CRITICAL accounts, counts how many times each issue type appeared in the last 90 days. 3+ occurrences = chronic.
- Account-file creation — optionally writes a per-account markdown file summarizing chronic issues (interactive; gated behind the stdin prompt).
- Trend history — appends a portfolio-level summary row and per-account rows to the history tabs every run.
Chronic-Issue Handling (why we pipe "no")
When chronic issues are detected the script prompts (input()) before writing results. Piping "no" answers that prompt, skips the optional file-creation step, and guarantees the Sheet write runs — so history comparison and chronic detection (both printed before the prompt) are preserved while the run stays safe under non-interactive execution. Enable interactive file creation only when running the script in a real terminal.
Cached-Output Contract (HARD CONSTRAINT)
If you wire this into a daily briefing, the briefing runs read_latest_ads_checker.py --critical-only, which reads cached rows from a history tab — it does NOT re-run the audit. The contract:
- Tab: a per-account history tab (e.g.
Account History) - Date column:
Audit Date, format%Y-%m-%d %H:%M, filtered to the last 24 hours - Columns:
Portfolio,CID,Account Name,Overall Severity, thenDKI Count,AI Assets Count,Broken URLs Count,Disapprovals Count,Seasonal Count,URL Expansion Count,Auto-Apply Count,Inappropriate Count,Spelling Count,Irrelevance Count
ads_checker_audit.py must emit exactly these. Never change the tab name, date format, or column headers without updating the reader in lockstep — otherwise the briefing section goes blank with no error. The section only populates if an audit ran in the last 24 hours.
Step 4 — Interpret Results
| Severity | Meaning | Action | |---|---|---| | CRITICAL | Broken URLs, disapprovals, inappropriate content | Fix immediately | | HIGH | DKI, 5+ spelling, missing brand, seasonal, high-risk auto-apply, auto-create ON | Fix within 24h | | MEDIUM | AI assets, URL expansion, 1-4 spelling | Review this week | | OK | No issues | None |
Read the console for per-account severity + counts, the comparison sections (NEW/INCREASED/RESOLVED), and the chronic summary.
Step 5 — Summarize
ADS CHECKER AUDIT COMPLETE
SCOPE: {Portfolio/Account} ACCOUNTS: {X} DATE: {now}
SEVERITY: CRITICAL {X} · HIGH {X} · MEDIUM {X} · OK {X}
CHANGES vs LAST RUN: NEW {X} · INCREASED {X} · RESOLVED {X} CHRONIC (3+/90d): {X}
TOP ISSUES: 1. {type}: {X} 2. {type}: {X} 3. {type}: {X}
NEEDS IMMEDIATE ATTENTION:
1. {Account} — {Severity} — {primary issue}
OUTPUT: — tabs: Raw Output · Account History · History
Output Configuration
| Setting | Value | |---|---| | Sheet ID | ` | | Tabs written | Raw Output (full detail), an optional per-portfolio tab, History (portfolio trend), Account History (per-account trend — the briefing source) | | Login customer ID | ` |
Error Handling
| Symptom | Cause | Fix | |---|---|---| | Script hangs / crashes mid-run | Chronic-issue input() prompt | Ensure echo "no" \| is piped in (Step 2) | | History tab not updated | Crash before the Sheet write, or --dry-run used | Re-run without --dry-run, with "no" piped | | Sheet write error | OAuth token expired | Re-authenticate | | Spell-checker import error | Spell-check dependency not installed | Install your spell-check library (e.g. pyspellchecker) |
Important Notes
- Read-only on Google Ads — never mutates accounts; only writes the audit Sheet.
- Don't change the cached contract (tab name / date format / column headers) without updating the reader.
- A daily briefing's creative-compliance section reflects audits from the last 24h only; if blank, run the audit first.
Invocation Patterns
Inline: "Run ads checker for [portfolio]" · "Creative audit for [account]" · "Ads checker for 1234567890"
Parallel (multiple portfolios at once):
Task(subagent_type="general-purpose", prompt="Use the ads-checker skill to audit the portfolio…")
A daily briefing does not invoke this skill — it reads cached results via read_latest_ads_checker.py. Generating fresh cached data requires running this skill (ad hoc or scheduled).
License
MIT — use freely in your own brain / repo / agency.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: fourteenwm
- Source: fourteenwm/ppc-ai-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.