# Marketing Skills — Full Content Concatenated markdown for all published skills on https://www.marketingskills.sh. --- # Diagnose landing page conversion problems for Google Ads traffic Separate tracking failures from UX problems in Google Ads conversion paths. Diagnose the real reason landing pages aren't converting. **Canonical:** https://www.marketingskills.sh/TheMattBerman/google-ads-copilot/google-ads-landing-review **Source:** https://github.com/TheMattBerman/google-ads-copilot/tree/main/skills/google-ads-landing-review **Category:** Paid Ads **Score:** 5.0 **Tags:** google-ads, landing-pages, conversion-tracking, diagnostics, path-analysis, ux-testing --- name: google-ads-landing-review description: > Diagnose landing page → conversion path problems for Google Ads traffic. Separates tracking failures from UX/path failures — the two most commonly confused sources of "low conversion rate." Pulls ad and campaign data via MCP, then reviews landing pages via browser or URL fetch to assess message match, load issues, form friction, and conversion path completeness. Produces a landing-review draft when problems are found. argument-hint: "[campaign name or URL]" --- # Google Ads Landing Review ## Why this skill exists "The landing page isn't converting" is the most common complaint in Google Ads. But it hides two completely different root causes: 1. **Tracking problem** — conversions ARE happening but aren't being counted. 2. **Path problem** — the visitor arrives but the page fails them. Most operators (and most clients) assume path failure when the numbers look bad. Half the time, it's a tracking failure. Conflating the two wastes months. This skill separates them. Read first: - `google-ads/references/operator-thesis.md` - `google-ads/references/tracking-playbook.md` - `google-ads/references/structure-playbook.md` Read workspace if available: - `workspace/ads/account.md` - `workspace/ads/goals.md` - `workspace/ads/findings.md` - `workspace/ads/drafts/_index.md` — check for existing tracking drafts --- ## Diagnostic Model: Two Forks ``` "Landing page isn't converting" │ ┌────┴────┐ │ │ FORK A FORK B Tracking Path/UX │ │ Is the Is the page signal actually correct? failing? ``` **Always run Fork A first.** If tracking is broken, Fork B conclusions are unreliable. --- ## Fork A: Tracking Diagnosis (Is the signal trustworthy?) ### What to check **1. Does a conversion action exist for the goal on this page?** - Is there a conversion action configured for the form/call/purchase that this landing page is supposed to produce? - Is it set as primary (`include_in_conversions_metric = TRUE`)? - Is the counting type correct? **2. Does the tag actually fire?** - Is the Google Ads conversion tag (or GA4 event imported to Google Ads) present on the confirmation/thank-you page? - Does the tag fire when the user completes the action? (Google Tag Assistant, network tab, or manual test) - Is the tag firing on the WRONG page? (e.g., firing on page load of the form page instead of the thank-you page) **3. Is the conversion path end-to-end intact?** - Click on ad → landing page → form/CTA → thank-you page → tag fires → conversion recorded - Where does the chain break? **4. Cross-domain / redirect issues?** - Does the landing page redirect to a different domain for the form/checkout? - If so, is cross-domain tracking configured? - Are UTM parameters / GCLID surviving the redirect? **5. Auto-tagging?** - Is auto-tagging enabled? - Are there URL parameters being stripped by the landing page CMS or CDN? **6. Attribution window?** - Conversion window too short (e.g., 1 day for a B2B lead that takes a week to decide)? - Multiple touchpoints lost? ### Fork A Data Acquisition (Connected Mode) **Conversion actions for this campaign:** ```sql SELECT conversion_action.name, conversion_action.type, conversion_action.category, conversion_action.counting_type, conversion_action.include_in_conversions_metric, conversion_action.status, metrics.conversions, metrics.all_conversions FROM conversion_action WHERE segments.date DURING LAST_30_DAYS AND conversion_action.status = 'ENABLED' ORDER BY metrics.conversions DESC ``` **Campaign-level conversion data:** ```sql SELECT campaign.name, campaign.final_url_suffix, metrics.clicks, metrics.conversions, metrics.all_conversions, metrics.cost_micros, metrics.cost_per_conversion FROM campaign WHERE campaign.status = 'ENABLED' AND segments.date DURING LAST_30_DAYS ORDER BY metrics.clicks DESC ``` **Ad-level landing page URLs and performance:** ```sql SELECT campaign.name, ad_group.name, ad_group_ad.ad.final_urls, ad_group_ad.ad.type, metrics.clicks, metrics.impressions, metrics.conversions, metrics.cost_micros FROM ad_group_ad WHERE campaign.status = 'ENABLED' AND ad_group_ad.status = 'ENABLED' AND segments.date DURING LAST_30_DAYS ORDER BY metrics.clicks DESC LIMIT 50 ``` **Landing page experience (quality score indicators):** ```sql SELECT campaign.name, ad_group.name, ad_group_criterion.keyword.text, ad_group_criterion.quality_info.quality_score, ad_group_criterion.quality_info.post_click_quality_score, ad_group_criterion.quality_info.creative_quality_score, ad_group_criterion.quality_info.search_predicted_ctr FROM keyword_view WHERE campaign.status = 'ENABLED' AND ad_group.status = 'ENABLED' AND ad_group_criterion.status = 'ENABLED' ORDER BY ad_group_criterion.quality_info.post_click_quality_score ASC LIMIT 50 ``` ### Fork A Verdict | Tracking Status | Meaning | Next Step | |----------------|---------|-----------| | **Clean** | Tag fires correctly, conversion action configured right, GCLID passes | Proceed to Fork B | | **Suspicious** | Tag exists but volume seems too low or too high vs. reality | Investigate specific break, then Fork B | | **Broken** | No tag, wrong tag, or GCLID stripped | Fix tracking FIRST. Fork B is premature. | | **Unknown** | Can't verify from API alone — needs manual tag inspection | Recommend Tag Assistant audit, then Fork B | --- ## Fork B: Path/UX Diagnosis (Is the page actually failing?) Only meaningful if Fork A shows tracking is Clean or Suspicious. ### The Message Match Test **1. Search intent → Ad promise → Landing page delivery** The #1 conversion killer in Google Ads is message mismatch: - User searches "roll-off dumpster rental near me" (specific, purchase-intent) - Ad says "Container Solutions for Your Business" (vague) - Landing page is a generic homepage with 12 menu items Each handoff is a potential drop: | Handoff | Question | Failure Mode | |---------|----------|-------------| | Search → Ad | Does the ad answer the specific search? | Generic ad for specific intent | | Ad → Landing | Does the LP deliver what the ad promised? | "Request a Quote" ad → page with no form | | Landing → CTA | Is the CTA visible, clear, and low-friction? | Form buried below fold, 15 fields | | CTA → Completion | Can the user actually complete the action? | Broken form, redirect fails, captcha blocks | **2. Specific checks (via browser or URL fetch):** - **Above-the-fold message:** Does the H1/hero text match the ad's promise? Does it match the search intent? - **CTA visibility:** Can the user see what to do within 3 seconds? Is the CTA above the fold? - **Form friction:** How many fields? Required fields? Captcha? Multi-step? - **Mobile experience:** Does it work on mobile? (Most Google Ads clicks are mobile) - **Page speed:** Does it load in <3 seconds? (Slow = bounced) - **Trust signals:** Phone number, reviews, certifications, real photos? - **Specificity:** Does the page serve ONE intent, or is it a homepage trying to serve all intents? ### The Intent Routing Test **3. Are different intent classes landing on different pages?** | Intent Class | Should Land On | Common Failure | |-------------|----------------|---------------| | Buyer ("buy X now") | Product/service page with CTA | Homepage | | Comparison ("X vs Y") | Comparison content | Product page with no comparison | | Research ("how does X work") | Educational content | Sales page | | Local ("X near me") | Location/service area page | National homepage | | Brand ("company name") | Homepage or brand page | Generic product page | If multiple intent classes all route to the same generic page, that's a structure problem (→ recommend in structure draft), not a landing page problem. ### The Conversion Path Walk **4. Walk the actual path the user takes:** ``` Click on ad → Landing page loads (check: speed, mobile rendering) → User reads headline (check: message match) → User finds CTA (check: visibility, clarity) → User clicks CTA (check: does it work?) → Form/checkout loads (check: friction, fields) → User submits (check: confirmation page loads) → Tag fires (check: conversion recorded) ``` Each step is a potential break. Document where the break is. ### Fork B Scoring Rate each dimension: | Dimension | Score | Notes | |-----------|-------|-------| | Message match (search→ad→page) | Strong / Weak / Missing | | | CTA clarity | Clear / Buried / Missing | | | Form friction | Low (≤4 fields) / Medium (5-8) / High (9+) | | | Mobile experience | Good / Adequate / Broken | | | Page speed | Fast (<3s) / Slow (3-6s) / Broken (>6s) | | | Trust signals | Strong / Some / None | | | Intent specificity | Focused / Mixed / Generic | | | Conversion path completeness | Complete / Partially broken / Broken | | --- ## Data Acquisition — Landing Page Review ### Connected Mode (MCP + Browser/Fetch) 1. Pull ad final URLs and campaign data via GAQL (see queries above) 2. For each unique landing URL: - Fetch with `web_fetch` for content analysis (H1, CTA text, form fields, page structure) - Use `browser` for interactive checks if needed (JavaScript-rendered pages, form testing) - Check mobile rendering if concerns arise 3. Cross-reference landing pages against search term intent classes from `workspace/ads/intent-map.md` ### Export Mode Ask the user for: - Landing page URL(s) - Which campaigns/ad groups point to which pages - Conversion action name and how it fires (page load, event, etc.) - Any known issues (form complaints, mobile problems) - Recent conversion volume (or "we don't know" — that's data too) --- ## Differential Diagnosis Summary After running both forks, produce a clear classification: ### Scenario 1: Tracking Problem Masquerading as UX Problem **Symptoms:** Low/zero conversions, but page looks fine, form works, users seem to engage **Diagnosis:** Tag not firing, wrong conversion action, GCLID stripped, cross-domain break **Action:** Fix tracking → then reassess conversion rate with clean data **Draft type:** Tracking fix (use `drafts/templates/tracking-draft.md`) ### Scenario 2: UX/Path Problem (Tracking Is Fine) **Symptoms:** Tracking verified clean, but conversion rate is genuinely low **Diagnosis:** Message mismatch, buried CTA, excessive form friction, wrong page for intent **Action:** Landing page improvements or intent routing changes **Draft type:** Landing review draft (use template below) and/or structure draft ### Scenario 3: Both Problems **Symptoms:** Tracking has issues AND the page has UX problems **Diagnosis:** Two independent failures compounding **Action:** Fix tracking first (P0), then address UX (P1) — in that order **Draft types:** Tracking fix draft + Landing review draft ### Scenario 4: Traffic Quality Problem (Page and Tracking Are Fine) **Symptoms:** Tracking clean, page is good, but conversion rate still low **Diagnosis:** The traffic is wrong — keywords matching wrong intent, broad match pulling junk, PMax sending Display/YouTube traffic to a Search landing page **Action:** Search terms analysis, negative keywords, or structure changes **Draft type:** Negative draft and/or structure draft (this is NOT a landing page problem) --- ## Draft Output ### Landing Review Draft **Trigger:** Fork B finds meaningful path/UX issues (at least 2 dimensions scored Weak/Missing/Broken) Create using the template below: - Write to `workspace/ads/drafts/YYYY-MM-DD-[account-slug]-landing-review.md` - Update `workspace/ads/drafts/_index.md` If Fork A also finds issues, create a SEPARATE tracking fix draft — don't mix them. ### Landing Review Draft Template ```markdown # Draft: Landing Page Review — [DATE] Status: proposed Skill: /google-ads landing-review Account: [Customer ID / Name] ## Summary [One paragraph: which pages were reviewed, the primary diagnosis (tracking vs path vs both), and the highest-priority fix.] ## Diagnostic Classification **Primary issue:** Tracking Problem | Path/UX Problem | Both | Traffic Quality Problem ## Fork A: Tracking Status - **Conversion action:** [Name and status] - **Tag status:** [Fires correctly / Suspicious / Broken / Unknown] - **GCLID passing:** [Yes / No / Unknown] - **Auto-tagging:** [Enabled / Disabled] - **Verdict:** [Clean / Suspicious / Broken / Unknown] ## Fork B: Path/UX Assessment ### Page: [URL] - **Campaigns pointing here:** [list] - **Clicks (30d):** [N] - **Conversions (30d):** [N] - **Implied conversion rate:** [X%] #### Scores | Dimension | Score | Detail | |-----------|-------|--------| | Message match | [Strong/Weak/Missing] | [specific observation] | | CTA clarity | [Clear/Buried/Missing] | [specific observation] | | Form friction | [Low/Medium/High] | [field count, issues] | | Mobile experience | [Good/Adequate/Broken] | [specific observation] | | Page speed | [Fast/Slow/Broken] | [load time if available] | | Trust signals | [Strong/Some/None] | [specific observation] | | Intent specificity | [Focused/Mixed/Generic] | [specific observation] | | Path completeness | [Complete/Partial/Broken] | [where it breaks] | ### Proposed Changes #### Change 1: [Specific recommendation] - **Current state:** [what's wrong] - **Proposed state:** [what to do] - **Expected impact:** [on conversion rate, quality score] - **Risk:** [what could go wrong] - **Priority:** P0 / P1 / P2 [repeat for each change] ## Intent Routing Assessment - **Are different intent classes landing on the right pages?** [Yes / No — detail] - **Should new landing pages be created?** [If so, for which intent classes] - **Cross-reference with intent map:** [link to workspace/ads/intent-map.md findings] ## Dependencies - [e.g., "Fix tracking (2026-03-15-acme-tracking-fix.md) before assessing conversion rate improvement"] ## Confidence [High / Medium / Low] — [reasoning] ## Review - [ ] Evidence checked - [ ] Collateral risk checked - [ ] Dependencies checked - **Decision:** approve | defer | reject - **Decision reason:** ____ - **Reviewed by:** ____ - **Reviewed on:** ____ - **Applied on:** ____ - **Notes:** ____ ``` ### Always update workspace memory: - `workspace/ads/findings.md` — landing page diagnosis and classification - `workspace/ads/learnings.md` — what we learned about this account's conversion path - Update existing tracking drafts if Fork A reveals new tracking problems --- ## Output Shape 1. **Account Status block** — name, CID, mode, date range, tracking confidence 2. **Diagnostic classification** — tracking problem vs path problem vs both vs traffic quality 3. **Fork A summary** — tracking status for each page/campaign reviewed 4. **Fork B summary** — path/UX scores for each landing page reviewed 5. **Differential diagnosis** — which scenario applies (1, 2, 3, or 4) 6. **Prioritized recommendations** — fix order matters (always tracking before UX) 7. **Drafts created** — with file paths and summaries 8. **Memory updates** --- ## Rules - **Always run Fork A first.** If tracking is broken, do NOT produce detailed UX recommendations — they will be based on phantom conversion data. - **Distinguish clearly:** A page with a broken tag and 0% conversion rate is a tracking problem, not a UX problem. Say it explicitly. - **Don't blame the landing page for traffic quality problems.** If the keywords are sending the wrong people, the page can be perfect and still not convert. Route to negatives/structure skills. - **Walk the actual path.** Don't just look at the page — follow the entire click → conversion chain. - **Be specific.** "The form has too many fields" is vague. "The form has 12 required fields including 'company size' and 'annual revenue' which are unnecessary for an initial quote request" is useful. - **Mobile first.** Most Google Ads clicks are mobile. If you can only check one thing, check mobile. - **One page at a time.** Don't try to review 10 pages at once. Start with the highest-spend page. - **Message match is almost always the problem.** When in doubt, check whether the H1 matches the search intent. If there's a mismatch, that's usually the answer. --- # Design structured interview processes and rubrics Build hiring processes with consistent rubrics, coding challenges, and calibration. Improve hiring signal and reduce bias. **Canonical:** https://www.marketingskills.sh/AbsolutelySkilled/AbsolutelySkilled/interview-design **Source:** https://github.com/AbsolutelySkilled/AbsolutelySkilled/tree/main/skills/interview-design **Score:** 5.0 **Tags:** hiring, interview-design, rubrics, assessment, recruiting-ops --- name: interview-design version: 0.1.0 description: > Use this skill when designing structured interviews, creating rubrics, building coding challenges, or assessing culture fit. Triggers on interview design, rubrics, scoring criteria, coding challenges, behavioral interviews, system design interviews, culture fit assessment, and any task requiring interview process design or evaluation criteria. category: operations tags: [interviews, rubrics, coding-challenges, hiring, assessment] recommended_skills: [recruiting-ops, technical-interviewing, performance-management] platforms: - claude-code - gemini-cli - openai-codex license: MIT maintainers: - github: maddhruv --- When this skill is activated, always start your first response with the 🧢 emoji. # Interview Design Structured interview design is the discipline of building hiring processes that produce consistent, defensible, and predictive hiring decisions. The core insight is that unstructured conversations are notoriously unreliable predictors of job performance - structured processes with explicit rubrics dramatically improve both accuracy and fairness. This skill covers the full lifecycle: scoping the interview loop, writing rubrics, building coding challenges, calibrating interviewers, and running debriefs that lead to confident decisions. --- ## When to use this skill Trigger this skill when the user: - Needs to design an interview loop or process for a role - Wants to create scoring rubrics or evaluation criteria - Asks how to build a coding challenge or take-home assignment - Needs help writing behavioral interview questions - Wants to design a system design interview round - Is trying to assess culture fit in a structured, defensible way - Needs to run calibration sessions with a panel - Asks how to run an effective debrief meeting Do NOT trigger this skill for: - Preparing as a candidate to pass interviews (different audience, different goal) - Compensation benchmarking or offer negotiation (use a compensation skill instead) --- ## Key principles 1. **Structured beats unstructured** - Consistent questions asked in the same order with pre-defined scoring criteria outperform free-form conversations every time. Interviewers who "go with their gut" introduce bias, not signal. 2. **Score independently before debrief** - Every interviewer must submit a written score and evidence summary before the panel debrief. Verbal-only debrief allows the first strong opinion to anchor everyone else. Written scores first. 3. **Test for the actual job** - Every interview exercise should map to a real task the candidate will perform in the role. If a backend engineer will never sort arrays on the job, don't test array sorting in isolation. Use job-relevant problems. 4. **Rubrics prevent drift** - Without a rubric, two interviewers evaluating the same candidate will produce wildly different scores. A rubric aligns on what "strong" and "weak" looks like before the first candidate walks in. 5. **Debrief is where decisions happen** - The debrief meeting is not a vote-counting exercise. It is a structured discussion to surface new evidence, resolve disagreements, and reach a confident collective judgment. The hiring manager owns the final call. --- ## Core concepts **Interview types** map to different evaluation needs. Coding interviews assess problem-solving and technical mechanics. System design interviews assess architectural thinking at scale. Behavioral interviews (using STAR) assess past behavior as a proxy for future behavior. Values/culture interviews assess alignment with how the team operates. Take-homes assess real-world execution and follow-through. Most loops include 3-5 rounds covering different dimensions so no single round carries all the weight. **Rubric design** is the practice of defining expected performance at multiple levels (typically 1-4 or Strong No / No / Yes / Strong Yes) before interviews begin. A good rubric specifies concrete behaviors, not adjectives. "Breaks problem into subproblems, names variables clearly, asks clarifying questions before coding" is a rubric. "Good technical skills" is not. See `references/rubric-templates.md` for ready-to-use rubric templates. **Signal vs noise** distinguishes real predictors of job performance from irrelevant factors. Signal: how a candidate structures ambiguity, responds to hints, explains trade-offs. Noise: how polished their communication style is, whether they went to a brand-name school, how quickly they reached the solution. Train interviewers to write down evidence (what the candidate said/did) rather than impressions ("seemed smart"). **Calibration** is the practice of running mock interviews with known candidates (or invented personas) so interviewers practice applying the rubric consistently before live interviews begin. A calibration session where two interviewers score the same response and then compare notes surfaces misalignment early. --- ## Common tasks ### Design a structured interview loop Start by mapping the role's core competencies - typically 4-6 dimensions that predict success. Common dimensions for engineering roles: | Dimension | Who covers it | |---|---| | Technical fundamentals | Coding round 1 | | System design / architecture | System design round | | Problem-solving approach | Coding round 2 | | Collaboration / communication | Bar raiser or cross-functional | | Values and culture | Hiring manager or peer | | Past impact and trajectory | Behavioral / resume deep-dive | Rules for a well-designed loop: - Every dimension is covered by exactly one round (no redundancy) - No interviewer covers more than one dimension (keeps each fresh) - The loop can be completed in one business day on-site or two days virtual - Assign a "bar raiser" - someone outside the immediate team with veto power ### Create scoring rubrics - template Use a 4-level rubric for each dimension. The key is defining the middle levels precisely - candidates cluster there, and those are the hard decisions. ``` Dimension: [Name, e.g., "Problem Decomposition"] Weight: [High / Medium / Low] 4 - Strong Yes Candidate independently breaks problem into clean subproblems. Names intermediate data structures without prompting. Explains trade-offs of multiple approaches before choosing. Handles edge cases proactively. 3 - Yes Candidate breaks problem into subproblems with minor prompting. Solves the core problem correctly. Handles most edge cases when prompted. Explains the primary trade-off. 2 - No Candidate solves simple version but struggles to generalize. Requires significant prompting to identify subproblems. Misses important edge cases. Does not discuss trade-offs unless directly asked. 1 - Strong No Candidate cannot decompose the problem independently. Solution is incorrect or incomplete. Does not respond to hints. Cannot explain what their own code does. ``` See `references/rubric-templates.md` for complete rubrics for coding, system design, behavioral, and culture fit rounds. ### Build a take-home coding challenge Take-homes reveal real-world execution that 45-minute whiteboard problems cannot. Design one that: - **Scopes to 2-3 hours max** - Respect candidate time. If it takes a senior engineer 2 hours, calibrate down. State the expected time in the instructions. - **Uses a realistic problem** - "Build a rate limiter for our API" beats "implement a binary search tree." Domain-adjacent problems reveal how candidates think about the actual work. - **Provides a starter repo** - Give candidates a repo with the scaffolding, CI, and test runner already wired. Evaluating candidates on setup skills is noise. - **Defines evaluation criteria upfront** - Include a `EVALUATION.md` in the repo that lists exactly what reviewers will look for: correctness, test coverage, code clarity, README quality. - **Has a follow-up interview** - Schedule a 30-minute code walkthrough. This prevents submitting work that isn't the candidate's own and surfaces how they think about their own decisions. **Evaluation checklist for reviewers:** - Does the solution solve the stated problem? - Are edge cases handled? - Is the code readable without explanation? - Are there tests, and are they meaningful? - Does the README explain design decisions? - Are there obvious improvements the candidate noted themselves? ### Design behavioral interview questions - STAR format Behavioral questions follow the pattern: "Tell me about a time when..." The STAR framework (Situation, Task, Action, Result) gives candidates a structure and gives interviewers a rubric for what a complete answer looks like. **Writing strong behavioral questions:** - Anchor to a specific competency (e.g., "conflict resolution" or "driving alignment without authority") - Phrase as past behavior, not hypothetical: "Tell me about a time you disagreed with your manager" not "What would you do if..." - Prepare follow-up probes in advance | Competency | Primary question | Follow-up probe | |---|---|---| | Handling ambiguity | Tell me about a project where the requirements were unclear. How did you proceed? | What would you do differently? | | Driving impact | Tell me about the highest-impact project you've worked on. What made it high-impact? | How did you measure that impact? | | Conflict resolution | Tell me about a time you had a serious technical disagreement with a peer. | How was it resolved? | | Prioritization | Tell me about a time you had more work than you could finish. | What did you drop, and how did you decide? | | Ownership | Tell me about something that went wrong on a project you led. | What did you change afterward? | **Scoring STAR responses:** - **Situation/Task** - Is the context clear and relevant to the role? - **Action** - Did the candidate describe their specific actions (not "we")? - **Result** - Is there a concrete, quantified outcome? - **Learning** - Does the candidate show reflection and growth? ### Design system design interviews System design interviews assess whether a candidate can architect solutions for real-world scale and ambiguity. The structure matters as much as the content. **Interview structure (45-60 minutes):** 1. **Requirements clarification** (5-10 min) - Candidate should ask scoping questions: scale, read/write ratio, latency requirements, consistency model. Award signal for good questions, not just correct answers. 2. **High-level design** (10-15 min) - Candidate draws the major components and data flows. Watch for separation of concerns and component boundaries. 3. **Deep dive** (15-20 min) - Interviewer picks one or two components to explore in depth: database schema, caching strategy, failure modes. 4. **Trade-offs and bottlenecks** (5-10 min) - Candidate explains what they would improve with more time, where the system might break, and why they made specific choices. **Rubric signals to watch:** - Does the candidate ask clarifying questions before whiteboarding? - Can they estimate load and justify their component choices with numbers? - Do they proactively identify single points of failure? - Can they explain the trade-off between consistency and availability? - Do they adjust the design when the interviewer changes a constraint? ### Run calibration sessions Calibration prevents rubric drift before it happens. Run one calibration session per new interviewer and one per quarter for existing panelists. **Calibration session format (60 minutes):** 1. Distribute a transcript or video of a mock interview (use a fabricated candidate, never a real one without consent) 2. Each interviewer scores independently using the rubric - no discussion yet 3. Reveal all scores simultaneously (prevents anchoring) 4. Discuss every dimension where scores diverge by 2+ points 5. Reach consensus on the "correct" score and the reasoning 6. Document the calibrated examples as reference cases for future interviewers **Red flags indicating calibration is needed:** - Two interviewers gave the same candidate a 4 and a 1 on the same dimension - An interviewer cannot cite specific evidence for their score - Scores correlate with candidate demographics, not candidate performance - The team has not hired anyone in 6 months despite many interviews ### Conduct effective debriefs The debrief is the most consequential 30-60 minutes in the hiring process. Run it badly and you amplify bias. Run it well and you surface the truth. **Before debrief:** - All interviewers submit written scorecards independently (hiring manager cannot see scores until all are submitted) - Block 48 hours maximum between last interview and debrief **Debrief agenda:** 1. Hiring manager reads all scorecards silently (5 min) 2. Each interviewer speaks to their dimension only - what evidence they saw, what level they scored, why (2 min per interviewer, no interruption) 3. Open discussion on dimensions with significant disagreement 4. Hiring manager asks: "Is there anything about this candidate we have not yet discussed that is relevant?" 5. Hiring manager states the decision and the primary evidence that drove it **Decision framework:** - Any Strong No from a bar raiser or domain expert is a block unless directly rebutted with evidence (not "they seemed nervous") - "Probably yes" is a No - only hire on conviction - Document the stated rationale in the ATS for every decision, hire or no-hire --- ## Anti-patterns | Anti-pattern | Why it fails | What to do instead | |---|---|---| | Gut-feel interviews | Interviewers cannot separate "I like them" from "they can do the job." Correlates with affinity bias, not job performance | Use structured questions and rubrics; require evidence-based scorecards | | Brainteaser questions | "How many golf balls fit in a school bus?" measures nothing relevant to engineering work. Banned at most major tech companies | Use problems derived from real work the candidate will actually do | | Group debrief without written scores | First speaker anchors the group. Quieter interviewers defer. The decision reflects seniority, not evidence | Require independent written scorecards before any verbal discussion | | Hiring bar creep | Interviewers gradually raise standards over months until no one is hireable, stalling team growth | Tie rubric levels to job requirements, not to the best candidate ever interviewed | | Same-style duplication | Two rounds both test the same coding dimension because neither interviewer was briefed on coverage | Map each dimension to exactly one round before the loop starts | | Culture fit as veto | "Not a culture fit" used as a catch-all rejection with no supporting evidence - often a proxy for bias | Define culture/values criteria explicitly in the rubric; require behavioral evidence | --- ## Gotchas 1. **Rubrics calibrated only on strong candidates produce grade inflation** - If interviewers see 10 qualified candidates in a row, their mental model of "a 3" drifts upward over time. Run calibration sessions quarterly using the same reference examples to anchor scores. Grade inflation causes you to reject good candidates because they score "only" a 3 when the bar has silently moved to 3.5. 2. **Take-home exercises without a time cap select for availability, not skill** - Without an explicit time cap, candidates who are between jobs or have no outside commitments will spend 12 hours on a "2-3 hour" challenge, and their submissions will look objectively better. State "we expect this to take 2-3 hours" in the instructions and calibrate your evaluation rubric to what a strong engineer can produce in that time. 3. **Allowing the hiring manager to see scores before all interviewers submit creates anchor bias** - If one interviewer submits a Strong Yes early and the hiring manager shares it in Slack, every subsequent scorer is subconsciously anchored. Enforce blind independent scoring: no interviewer sees anyone else's score or written feedback until all scorecards are submitted. 4. **Interview loops covering the same dimension twice produce inflated confidence** - Two coding rounds both asking algorithm questions don't double the signal - they create redundant data while leaving system design, communication, or values completely unevaluated. Map each round to a unique dimension before the first candidate interviews, and stick to the map. 5. **Debrief dominated by the most senior person in the room is not a debrief** - If an engineering director speaks first with a strong opinion, junior interviewers defer and the decision reflects the director's prior, not the collective evidence. The facilitator must ask every interviewer to present their evidence before any discussion begins, starting with the most junior voice. --- ## References For detailed content on specific topics, read the relevant file from `references/`: - `references/rubric-templates.md` - Ready-to-use scoring rubrics for coding, system design, behavioral, and culture fit rounds Only load a references file if the current task requires deep detail on that topic. --- ## Companion check > On first activation of this skill in a conversation: check which companion skills are installed by running `ls ~/.claude/skills/ ~/.agent/skills/ ~/.agents/skills/ .claude/skills/ .agent/skills/ .agents/skills/ 2>/dev/null`. Compare the results against the `recommended_skills` field in this file's frontmatter. For any that are missing, mention them once and offer to install: > ``` > npx skills add AbsolutelySkilled/AbsolutelySkilled --skill > ``` > Skip entirely if `recommended_skills` is empty or all companions are already installed. --- # Run comprehensive SEO and GEO audits with tailored action plans and execution Comprehensive site audits covering technical SEO, E-E-A-T, Core Web Vitals, schema, backlinks, and AI search—with ranked findings and phased roadmaps. **Canonical:** https://www.marketingskills.sh/mykpono/ultimate-seo-geo **Source:** https://github.com/mykpono/ultimate-seo-geo/tree/main/SKILL.md **Category:** SEO **Score:** 4.8 **Tags:** seo audit, technical seo, e-e-a-t, schema markup, geo, ai search --- name: ultimate-seo-geo description: Audits and optimizes websites for search engine visibility (SEO) and AI search citation (GEO), covering technical health, E-E-A-T content scoring, domain authority, structured data, rich results, and entity signals. Use when running SEO audits, diagnosing traffic drops or ranking losses, generating Schema.org JSON-LD, checking Core Web Vitals, crawlability, robots.txt, sitemaps, hreflang, backlinks, planning content strategy or site migrations, fixing indexing issues, or optimizing for AI Overviews, ChatGPT, and Perplexity. NOT for paid ads (PPC/SEM), social media strategy, email marketing, or general web development unrelated to search. version: 1.8.5 --- # Ultimate SEO + GEO — LLM-Agnostic SEO Agent | Attribute | Details | | --- | --- | | **Version** | 1.8.5 | | **Updated** | 2026-04-11 | | **License** | MIT | | **Author** | Myk Pono | | **Lab** | [lab.mykpono.com](https://lab.mykpono.com) | | **Homepage** | [lab.mykpono.com](https://lab.mykpono.com) | | **LinkedIn** | [Profile](https://www.linkedin.com/in/mykolaponomarenko/) | | **Platforms** | Claude Code, Cursor, Copilot, Gemini CLI, Codex, Windsurf, Cline, Aider, Devin | The definitive SEO and Generative Engine Optimization agent. LLM-agnostic — works on any platform that reads `AGENTS.md`. Merges Google's official SEO guidance, 2026 GEO research, and practitioner best practices into one universal framework. Every finding comes with a clear fix directive — not just diagnosis. **This file is the routing shell.** Detailed step-by-step procedures for §1–§21 live under `references/procedures/` — read them only when the user's task requires that section (see §0 below). Domain knowledge tables live in `references/*.md` as before. ## 0. Before You Start ### Routing index (read only what you need) | Goal | Procedure file(s) | Also read / run | |------|-------------------|-----------------| | Full scored audit | `references/procedures/02-full-site-audit.md`, `references/procedures/21-script-toolbox.md` | `references/audit-script-matrix.md`, `generate_report.py` | | AI citations / GEO | `references/procedures/03-geo-ai-search.md` | `references/ai-search-geo.md`, `entity_checker.py`, `llms_txt_checker.py`, `robots_checker.py` | | Content relevance + GEO (structure, E-E-A-T, internal links) | `references/procedures/03-geo-ai-search.md`, `references/procedures/06-content-eeat-and-pruning.md` | `references/eeat-framework.md`, `article_seo.py`, `readability.py`, `internal_links.py`, `generate_report.py` | | Schema only | `references/procedures/05-schema-structured-data.md` | `references/schema-types.md`, `validate_schema.py` | | Local | `references/procedures/12-local-seo.md` | `references/local-seo.md`, `local_signals_checker.py` | | Crawl / index / performance | `references/procedures/04-technical-seo.md`, `references/procedures/11-crawl-indexation.md` | Matrix scripts (`robots_checker`, `sitemap_checker`, `pagespeed.py` if API works) | | Migration | `references/procedures/20-site-migration.md` | `references/site-migration.md`, `redirect_checker.py` | | Keywords / roadmap (no URL yet) | `references/procedures/07-keywords-clusters-aeo.md`, `references/procedures/16-strategy-roadmap.md` | **Do not** invent a live-site `/100` score | Section numbers **§1–§21** match `AGENTS.md` and the filenames in `references/procedures/`. Full index: [`references/procedures/README.md`](references/procedures/README.md). ### Reference Reading Guide When a section points to a reference file, read only what you need for the current task. **Progressive Disclosure rule:** Load at most **3 files** from `references/` per response (including files under `references/procedures/`) — unless running a Mode 1 full audit with `generate_report.py`, which implicitly covers all dimensions. For single-topic Mode 2 or Mode 3 tasks (e.g., "fix my schema", "write an llms.txt"), the routing tables identify 1–2 topical `references/*.md` files plus **at most one** `references/procedures/*.md` file when procedural detail is required. Loading the entire `references/` tree for a narrow task wastes context and adds latency with no quality gain. This pattern follows Anthropic's [Skills progressive disclosure architecture](https://github.com/anthropics/claude-cookbooks/tree/main/skills). | Task | Read | Run | |------|------|-----| | Full audit (any type) | `references/audit-script-matrix.md` | `generate_report.py` | | GEO / AI citations | `references/ai-search-geo.md`, `references/entity-optimization.md` | `robots_checker.py`, `entity_checker.py`, `llms_txt_checker.py` | | Schema markup | `references/schema-types.md` | `validate_schema.py` | | Technical / CWV | `references/technical-checklist.md` | `pagespeed.py`, `robots_checker.py`, `security_headers.py` | | Content / E-E-A-T | `references/eeat-framework.md`, `references/core-eeat-framework.md` | `readability.py`, `article_seo.py` | | CITE domain audit | `references/cite-domain-rating.md` | `link_profile.py` | | Keywords / clusters | `references/keyword-strategy.md` | — | | Links | `references/link-building.md` | `internal_links.py`, `broken_links.py`, `link_profile.py` | | Local SEO | `references/local-seo.md` | `local_signals_checker.py` | | Images | `references/image-seo.md` | `image_checker.py` | | International / hreflang | `references/international-seo.md` | `hreflang_checker.py` | | Programmatic SEO | `references/programmatic-seo.md` | `programmatic_seo_auditor.py` | | Migration | `references/site-migration.md` | `redirect_checker.py` | | Analytics / myths | `references/analytics-reporting.md` | — | | Crawl / indexation | `references/crawl-indexation.md` | `sitemap_checker.py`, `duplicate_content.py`, `canonical_checker.py`, `broken_links.py`, `internal_links.py` | ### When *not* to run Mode 1 (full audit) | User signal | Action | |---------------|--------| | **Google Ads / PPC** as the primary ask | Paid-media scope — no organic SEO Health Score or crawl Finding wall unless organic SEO is also requested. | | Employer branding only, **pure** press/PR distribution, **email-only** marketing | Narrow guidance; no implied full technical + content audit. | | **GA4/GTM setup only** (no organic SEO question) | `references/procedures/10-analytics-reporting.md` — **no** fabricated domain-wide numeric score. | | **Social** community management only | Out of scope unless tied to organic discovery (e.g. `sameAs`, entity signals). | | **Explicitly scoped** task (e.g. “only robots.txt + sitemap”) | Stay in that scope — no domain-wide E-E-A-T essay or `/100` score unless the user asks. | ### Audit Context: Internal vs. Competitive Mode Before routing, determine which audit context applies. This controls what outputs are valid. | Signal | Context | What's Allowed | |---|---|---| | User says "my site", "our site", "I own", provides GSC/GA4 access, or confirms backend access | **Internal Mode** | Full scored audit, all 27 scripts eligible, Execute mode available, /100 Health Score valid | | External URL the user does not own (competitor, prospect, reference site) | **Competitive Mode** | Surface crawl only (homepage + up to 20 pages), no /100 Health Score, Execute mode disabled, all output labeled **"External Observation Only"** | **When in doubt, ask:** "Is this your site, or are you analyzing a competitor?" This skill operates in three modes. Identify which mode applies before touching anything else. ### The Three Modes **Mode 1 — Audit** Fetch the site, run all relevant checks, produce a scored report. Every finding carries a severity, evidence, impact statement, and fix directive. Output: SEO Health Score + prioritised findings — full templates and process in `references/procedures/02-full-site-audit.md`. **Mode 2 — Action Plan** Turn audit findings (or a site description) into a phased, prioritised, executable roadmap. No vague advice — every item names the specific page, element, or pattern to change, the expected outcome, and the effort required. Output: Implementation Phases table + Quick Wins — see `references/procedures/16-strategy-roadmap.md` and Mode 2 format in `references/procedures/02-full-site-audit.md`. **Mode 3 — Execute** Do the work. Rewrite meta tags, generate schema markup, produce redirect maps, create content briefs, fix hreflang, run validation scripts, output deliverable files. Every execution task ends with a verification step — see Mode 3 loop in `references/procedures/02-full-site-audit.md`. Most requests involve all three in sequence: **Audit → Plan → Execute**. Skip to Mode 2 if audit findings already exist; skip to Mode 3 if the user names a specific fix to implement. ### Intake Checklist Three questions only — skip any already answered in the user's message. | # | Question | Why It Matters | |---|---|---| | 1 | **What is the URL?** | Required for all three modes | | 2 | **What is the primary goal?** (traffic / AI citations / local leads / traffic drop / specific keyword) | Determines which modules run first | | 3 | **Which mode?** Audit / Audit + Plan / Audit + Plan + Execute | Scopes the work — default to all three if unclear | Everything else (analytics access, CMS, business type) is discovered during the audit. ### Mode Routing ``` User request + URL │ ├─ "audit", "analyze", "full check", "what's wrong" │ └─ Mode 1 → read procedures/02-full-site-audit.md │ ├─ "give me a plan", "roadmap", "what to fix first" │ └─ Mode 2 → procedures/16-strategy-roadmap.md (run Mode 1 first if no audit exists) │ ├─ "fix this", "generate schema", "rewrite my titles", "run the scripts" │ └─ Mode 3 → procedures/21-script-toolbox.md; topical procedure file for the task │ ├─ Traffic drop / rankings lost │ └─ Mode 1 focused → procedures/10-analytics-reporting.md first, then procedures/06-content-eeat-and-pruning.md / procedures/04-technical-seo.md │ ├─ AI citations / GEO question │ └─ Mode 1 focused → procedures/03-geo-ai-search.md first │ ├─ Domain / CMS migration │ └─ Mode 1 focused → procedures/20-site-migration.md │ └─ No mode stated + URL / "audit + fix everything" └─ Mode 1 → 2 → 3 (procedures/02, then 16, then execute top findings) ``` Topic-to-section routing table: **`references/procedures/01-request-detection-routing.md`** (same content as former SKILL §1). ### What "Done" Looks Like per Mode **Audit complete** when: SEO Health Score delivered, all Critical and High findings documented in Finding/Evidence/Impact/Fix/Confidence format, no section skipped without reason stated. **Plan complete** when: findings grouped into four implementation phases (Foundation / Expansion / Scale / Authority), each item has an owner action, expected outcome, and effort estimate. **Execute complete** when: every fix implemented AND verified — run the relevant validation script, review the output, confirm it resolves the original finding. ### Context Budget Awareness If you are running on a model or configuration with limited context length or execution time (e.g., fast-model subagents, CI pipelines, or agentic chains), apply graceful degradation before hitting a wall: 1. **Estimate before executing.** A full Mode 1 audit with `generate_report.py` and all scripts can produce 50k+ tokens of output. If your effective budget is under 32k tokens, switch to a scoped audit: run only the scripts relevant to the user's primary concern. 2. **Prefer partial delivery over timeout.** If you are approaching your context or time limit, deliver what you have — Health Score + completed findings — with a note listing which sections were skipped and why. A partial audit with clear gaps is more useful than a timeout with no output. 3. **Web fetches are expensive.** Each site fetch adds latency and tokens. For scoped tasks (schema only, robots.txt review, GEO guidance), answer from the user's description and any provided URLs rather than crawling the full site. 4. **Compaction fallback.** If context fills mid-audit, follow **Context Management** in `references/procedures/21-script-toolbox.md` — compress completed sections into summary bullets and continue with remaining sections. This is a **fallback**, not a default. When context budget allows, always prefer the full audit pipeline. --- ## Global guardrails (always apply) These rules apply to every mode. **Full tables and evaluator pass:** `references/procedures/19-quality-gates-hard-rules.md`. ### Evidence integrity (do not claim without data) | Claim | Only state if | |---|---| | LCP / INP / CLS / performance score | `pagespeed.py` ran successfully, or user pasted PageSpeed Insights / CrUX output | | Backlink count or referring domains | `link_profile.py` ran and returned data | | Organic traffic or impression numbers | GSC / GA4 access confirmed and data retrieved | | Health Score /100 | Internal Mode + minimum 5 scripts ran with data | | Schema errors or validation status | `validate_schema.py` ran against the page | | Schema "not found" on a CMS site | Confirmed via Rich Results Test or browser JS — raw HTML cannot detect JS-injected schema | **When data is absent:** use `[metric] not measured — run [script] for actual data` or ask the user to provide it. ### Finding format (mandatory) Every finding: **Finding** / **Evidence** / **Impact** / **Fix** / **Confidence** (Confirmed / Likely / Hypothesis). Example report excerpt: `references/audit-output-example.md`. ### High-Risk execute gate **High-Risk** changes (robots.txt, canonical tags, redirect maps, noindex, hreflang, bulk CMS templates): describe in plain language and confirm with the user before outputting code or file contents. **Safe** changes (meta, alt text, most schema, content rewrites, llms.txt): may output directly. Full classification table: `references/procedures/02-full-site-audit.md` (Mode 3). ### Before delivering any Mode 1 audit Run the internal self-evaluation pass in `references/procedures/19-quality-gates-hard-rules.md` (Evaluator-Optimizer checklist). --- ## Procedure file index (§1–§21) | § | File | |---|------| | 1 | [`references/procedures/01-request-detection-routing.md`](references/procedures/01-request-detection-routing.md) | | 2 | [`references/procedures/02-full-site-audit.md`](references/procedures/02-full-site-audit.md) | | 3 | [`references/procedures/03-geo-ai-search.md`](references/procedures/03-geo-ai-search.md) | | 4 | [`references/procedures/04-technical-seo.md`](references/procedures/04-technical-seo.md) | | 5 | [`references/procedures/05-schema-structured-data.md`](references/procedures/05-schema-structured-data.md) | | 6–6b | [`references/procedures/06-content-eeat-and-pruning.md`](references/procedures/06-content-eeat-and-pruning.md) | | 7–7c | [`references/procedures/07-keywords-clusters-aeo.md`](references/procedures/07-keywords-clusters-aeo.md) | | 8 | [`references/procedures/08-competitor-analysis.md`](references/procedures/08-competitor-analysis.md) | | 9 | [`references/procedures/09-link-building-internal.md`](references/procedures/09-link-building-internal.md) | | 10 | [`references/procedures/10-analytics-reporting.md`](references/procedures/10-analytics-reporting.md) | | 11 | [`references/procedures/11-crawl-indexation.md`](references/procedures/11-crawl-indexation.md) | | 12 | [`references/procedures/12-local-seo.md`](references/procedures/12-local-seo.md) | | 13 | [`references/procedures/13-image-seo.md`](references/procedures/13-image-seo.md) | | 14 | [`references/procedures/14-international-hreflang.md`](references/procedures/14-international-hreflang.md) | | 15 | [`references/procedures/15-programmatic-seo.md`](references/procedures/15-programmatic-seo.md) | | 16 | [`references/procedures/16-strategy-roadmap.md`](references/procedures/16-strategy-roadmap.md) | | 17 | [`references/procedures/17-monthly-maintenance.md`](references/procedures/17-monthly-maintenance.md) | | 18 | [`references/procedures/18-myths.md`](references/procedures/18-myths.md) | | 19 | [`references/procedures/19-quality-gates-hard-rules.md`](references/procedures/19-quality-gates-hard-rules.md) | | 20 | [`references/procedures/20-site-migration.md`](references/procedures/20-site-migration.md) | | 21 | [`references/procedures/21-script-toolbox.md`](references/procedures/21-script-toolbox.md) | --- # Write short-form video scripts from audience pain points Research pain points, apply viral creator patterns, write hooks with 3 variations, group into series. **Canonical:** https://www.marketingskills.sh/coopersimson96/ai-content-system/content-scripting **Source:** https://github.com/coopersimson96/ai-content-system/tree/main/skills/content-scripting **Category:** Content Marketing **Score:** 4.8 **Tags:** short-form video, hook frameworks, content production, social media strategy, script writing, viral patterns --- name: content-scripting description: Full content production workflow — research real pain points, apply outlier content patterns, write short-form video scripts with hook variations, group into series. Also handles script refreshes and rewrites using Inspiration Library data. argument-hint: "[topic] for [audience]" or "rewrite [file]" or "refresh SF #3" or "research + write [number] scripts about [topic]" context: conversation --- # Short-Form Video Script Production System Full workflow from research to publish-ready scripts for TikTok, Reels, and YouTube Shorts. Every script is grounded in real audience pain, scored against an outlier content checklist derived from 10+ documented viral creators, and structured using proven hook frameworks from 16K+ social media engagements. **This is NOT a generic content generator.** Every script must be rooted in a real problem a real person expressed online. No hypothetical topics. --- ## Step 1: Parse the User's Input Extract from `$ARGUMENTS`: 1. **TOPIC** — The content niche (e.g., "AI for business owners", "fitness for beginners", "SaaS product demos") 2. **AUDIENCE** — Target viewer (e.g., "small business owners", "founders", "freelancers") 3. **COUNT** — How many scripts? (default: 10) 4. **PLATFORM** — TikTok, Reels, Shorts, or all? (default: all) 5. **TONE** — Professional, casual/raw, humorous, educational? (default: casual/raw) 6. **MODE** — What the user wants: - **"research + write"** (default) — Full workflow: research pain points, then write scripts - **"write from [pain points]"** — User provides pain points, skip research - **"rewrite [file]"** — Take existing scripts and apply outlier framework + hook variations - **"add hooks to [file]"** — Keep existing scripts, just add 3 hook variations each - **"refresh SF #N"** — Fetch existing script from Notion, generate fresh variations using new library data - **"new hooks for SF #N"** — Lightweight hook swap only, keep everything else **If TOPIC is missing**, ask: > What's the content about and who's it for? > > Example: `/scripts AI automation for small business owners` > Example: `/scripts rewrite "My Content.md"` **If only TOPIC is provided**, infer AUDIENCE and proceed. Don't over-ask. --- ## Step 2: Research Real Pain Points **CRITICAL: Skip this step ONLY if the user provides their own pain points or an existing file to rewrite.** ### Why This Step Exists Scripts based on real complaints from real people outperform hypothetical topics every time. The audience sees the hook and thinks "that's literally me" because it IS literally them — their exact words, their exact frustration. ### Research Process Use the `/last30days` skill or manual research to find burning problems: **Search queries to run (adapt to TOPIC):** 1. Reddit: `{TOPIC} frustration OR "waste time" OR "hate doing" OR "manual process" OR "how do I automate"` in relevant subreddits 2. X/Twitter: `{TOPIC} pain point OR complaint OR "wish there was" OR "spent hours" OR "waste of time"` 3. Web: `{TOPIC} biggest challenges survey`, `{TOPIC} time-consuming tasks`, `{TOPIC} automation opportunities` **What to extract from each source:** - The **exact quote** or paraphrase of the complaint - **Engagement metrics** (upvotes, likes, comments) — higher = more people feel this pain - **Specificity** — "I spend 3 hours routing technicians" beats "scheduling is hard" - **Dollar/time amounts** — real numbers make scripts credible ### Pain Point Scoring Rank discovered pain points by: | Signal | Weight | Why | |---|---|---| | Engagement (upvotes/likes) | HIGH | More people = bigger audience for this topic | | Specificity (exact numbers/scenarios) | HIGH | Specific = relatable = better hooks | | Solvability (can AI actually fix this?) | HIGH | Script must deliver a real solution | | Freshness (posted recently) | MEDIUM | Recency = currently top of mind | | Cross-platform appearance | MEDIUM | Shows up on Reddit AND X = universal pain | **Output: A ranked list of {COUNT} pain points, each with:** - One-line summary - Source + engagement data - The specific AI solution **Show this list to the user before writing scripts.** Get confirmation or let them swap topics. --- ## Step 3: Apply the Outlier Content Checklist Every script MUST pass ALL 12 checks before being included in the final output. This checklist is derived from documented outlier creators: - **Harper Carroll** — 2K → 200K Instagram followers in 3 weeks. Calm, educational, series-based. One video = 250K followers. - **Matt Wolfe** — 857K YouTube subscribers, 65.5M total views. Hooks in first 15-30s, open-ended titles. - **@ogilichev** — 60M views from repeating one hook format with tiny tweaks across 3 accounts. - **@timdanilovhi** — 70+ Shorts earning $1K-$7K each using the same ideation framework. - **@seergioo_gil** — 12M views from 4 Shorts by copying a proven viral format. ### Library Lookup (before scoring) Before applying the checklist, check if `~/.config/notion-content/config.json` has `inspiration_data_source_id`. If so: 1. Query the Inspiration Library using `notion-search` with `data_source_url` set to `collection://{inspiration_data_source_id}` for the current topic 2. Filter for: Type IN [Hook Example, Creator Pattern], Performance Tier IN [Outlier, Strong], limit 5 results 3. Format as compact injection block and treat as additional examples alongside the hardcoded patterns below: ``` INSPIRATION LIBRARY ({topic}): [1] "{Hook Text}" — @{creator} | {Tier} | {Engagement Metric} [2] "{Hook Text}" — @{creator} | {Tier} | {Engagement Metric} ``` 4. **Fallback:** If 0 results or config missing, proceed silently with hardcoded patterns only 5. Track which library entries were referenced (for Last Used update after push) ### THE 12-POINT OUTLIER CHECKLIST For every script, verify: - [ ] **1. Hook lands in under 3 seconds** — 71% of viewers decide in first 3s whether to keep watching - [ ] **2. Specific real-world proof** — Exact numbers, dollars, hours from real people. No vague claims. "3 hours → 5 minutes" not "saves you time" - [ ] **3. One tool, one problem** — Each script solves exactly ONE pain with ONE clear solution. Never cram multiple tools or problems into one video. - [ ] **4. Show-don't-tell** — Every script includes [SCREEN] cues showing the actual prompt, tool, or output. Screen recordings are the #1 format for educational content. - [ ] **5. Result first, method second** — Lead with the outcome. Proof-First hooks get 4-7x more impressions when paired with 65%+ 3-second retention. - [ ] **6. Relatable pain from real people** — The topic came from an actual complaint (Reddit post, X thread, forum), not a hypothetical scenario. - [ ] **7. Anti-hype tone** — No "AI will change everything." No overselling. Every claim is grounded in a specific person's real experience. Realistic framing builds trust. - [ ] **8. Series-ready** — Scripts are grouped into themed series of 3-5. Series format grew Harper Carroll 100x. Viewers binge series. - [ ] **9. Works silent** — 85% watch without sound. Every hook has bold text overlay. Every key point has on-screen text. - [ ] **10. Under 35 seconds** — Sweet spot is 25-35 seconds. Under 30s for new audiences. Speed implies simplicity — if it can be shorter, make it shorter. - [ ] **11. Scene changes every 2-4 seconds** — Alternate between [FACE] (talking head) and [SCREEN] (screen recording) to maintain retention. - [ ] **12. CTA builds funnel** — "Comment [WORD]" format on relevant scripts. Drives DMs → newsletter → community. **If a script fails any check, rewrite it until it passes.** --- ## Step 4: Select Hook Frameworks You have **10 proven hook frameworks** with documented performance data. For each script, generate **3 hook variations** using different frameworks. Choose the 3 most relevant — don't force-fit. ### THE 10 HOOK FRAMEWORKS **Tier 1 — Highest documented performance:** #### 1. Curiosity Gap / Open Loop Opens a knowledge gap the brain compulsively needs to close. 70%+ of viral videos use this. "Did you know..." template alone: 22M+ views documented. **Templates:** - "I'm about to show you something that [industry] doesn't want you to know." - "Here's what [experts/pros] use but never share publicly..." - "Nobody's talking about this, but [surprising claim]..." - "I found out why [common problem] keeps happening. And it's not what you think." #### 2. Proof-First / Results Hook Leads with evidence/outcome. Gets 4-7x more impressions with strong retention. @timdanilovhi earned $1K-7K per Short using this framework consistently. **Templates:** - "I [achieved result] in [timeframe]. Here's exactly how." - "This [tool/method] took us from [before state] to [after state]." - "[Specific number/metric]. That's what happened when I [did thing]." #### 3. Pain Point Hook Mirrors the audience's daily frustration. "3 mistakes everyone makes about X" template: documented 2x engagement vs generic advice. **Templates:** - "If you're still [painful manual process] in 2026 — we need to talk." - "How much time did you waste on [task] this week? Be honest." - "[Pain point] is costing you [money/time/customers]. Here's how to stop it." **Tier 2 — Strong performers:** #### 4. Contrarian / Bold Statement Challenges assumptions. Forces a pause because the brain wants to resolve the contradiction. **Templates:** - "Stop doing [common thing]. Do this instead." - "[Common advice] is actually terrible advice. Here's why." - "Everyone says [popular opinion]. They're wrong." #### 5. "This Cost Me Thousands" Personal stakes + financial consequences = high engagement. People lean in when real money is on the line. **Templates:** - "This one mistake cost me $[amount]. Don't repeat it." - "I was spending $[amount] a [time period] on [thing] until I found this." #### 6. The Unexpected Confession Feels personal and exclusive. Triggers curiosity when someone shares something "they shouldn't." **Templates:** - "I probably shouldn't share this, but..." - "I'm embarrassed it took me this long to figure this out." #### 7. Question Hook Creates information gaps. "Have you heard about..." template: 13M+ views, 83% higher comment rates. **Templates:** - "How many hours a [time period] do you spend on [task]? What if it was zero?" - "What do [successful people] all have in common? It's not [obvious thing]." **Tier 3 — Situational:** #### 8. Pattern Interrupt (Visual + Audio) Hijacks attention before conscious decision-making. Works even silent. **Templates:** - *[Show the end result FIRST]* + "This took me [short time]. Here's what I did." - *[Split screen: old way vs new way]* + "Same task. Different century." #### 9. Before vs After / Transformation Visually satisfying, shows value instantly. Outperforms specifically on Instagram Reels. **Templates:** - "[Before metric] vs [After metric]. Same [person/business]. One change." - *[Side-by-side comparison]* + "Left: [time ago]. Right: today." #### 10. Test / Experiment Hook "What happened" structure creates an open loop. Great for product demos. **Templates:** - "I tried [thing] for [time period]. Here's what happened." - "I tested [number] different [things] to see which one actually works." #### 11. Question / Challenge Hook Directly challenges the viewer to confront their own behavior or assumptions. Unlike the Question Hook (#7) which creates information gaps, this one puts the viewer on the spot — it demands self-reflection or issues a dare. Works best when the topic has a clear "you're probably doing this wrong" angle. **Templates:** - "Be honest — when's the last time you [did painful manual task]? What if I told you it takes 2 minutes now?" - "I challenge you to try this for one week. If it doesn't save you [time/money], I'll eat my words." - "Can you actually explain what your [tool/agency/employee] does for you? Or are you just paying and hoping?" - "How long are you going to keep doing [manual thing] before you let AI handle it?" **When to use:** Only when the topic naturally lends itself to calling out a behavior the viewer knows they're guilty of. The challenge must feel earned — backed by a real solution, not empty provocation. Pairs well with Proof-First (challenge → then show the proof) and Pain Point (challenge → then name the specific pain). ### Framework Selection Rules - **Proof-First** should appear in at least 40% of scripts (highest documented performance) - **Pain Point** should appear whenever the topic involves a specific frustration - **Curiosity Gap** works for nearly any topic — use as a reliable fallback - **Contrarian** works best when challenging common assumptions ("you don't need X") - **"This Cost Me Thousands"** only when real dollar amounts exist in the research - **Never use the same 3 frameworks for consecutive scripts** — vary the combinations ### Library Hook Examples (after selecting frameworks) After selecting 3 frameworks per script, query the Inspiration Library for concrete examples: - Search for: Type = `Hook Example`, Hook Framework = [each selected framework], Performance Tier IN [Outlier, Strong], limit 2 per framework - Use returned Hook Text as additional template variants alongside the hardcoded templates above - This gives you real-world proven hook text to draw from, not just generic templates --- ## Step 5: Script Structure Every script follows a **screen-first rapid walkthrough** style: Hook → Walkthrough → Result → CTA. No formal "teach" or "punch" sections — the walkthrough IS the punch. The walkthrough IS the teach. **Style reference:** @noevarner.ai — 319K views, 17.7K likes on a 31-second Claude Code tutorial. Ultra-casual, rapid-fire, screen-first. ### Visual Direction Tags Use these inline tags to indicate camera direction: - **[FACE]** — Talking head, camera on creator - **[SCREEN]** — Screen recording showing the actual tool/prompt/output **Default split: 90-95% [SCREEN], 5-10% [FACE].** Face appears at most twice — optionally at the hook and/or the close. The screen recording IS the content. Speed implies simplicity. **Text overlays go in a separate section at the bottom of each script** (not inline). This keeps the script body clean and readable as a filming guide. ### Platform Timing | Platform | Hook Window | Total Length | Energy | |---|---|---|---| | TikTok | 2 seconds MAX | 15-30 seconds | 20% higher than normal | | Reels | 3 seconds | 25-35 seconds | Casual but fast | | Shorts | 3-5 seconds | 25-35 seconds | Structured storytelling | ### Script Template ``` ## SF #{N} - {Title} **Platform:** {Platform} | **Target:** {length} seconds | **Tone:** {Tone description} **Style Reference:** {Creator or style inspiration if applicable} --- **HOOK A ({Framework Name}):** [SCREEN] {Hook text — must land within 2-3 seconds} **HOOK B ({Framework Name}):** [SCREEN] {Alternative hook for A/B testing} **HOOK C ({Framework Name}):** [FACE] {Third variation — face hooks are the exception, not the default} --- **SCRIPT (Hook A version — full walkthrough):** [SCREEN] {Hook line repeated — this is the actual filming script} [SCREEN] {Step 1 — show the action, narrate over it. "Open this. Type this."} [SCREEN] {Step 2 — keep moving fast. No pauses, no reflections.} [SCREEN] {Step 3 — show the tool working. Let the screen do the talking.} [SCREEN] {Step 4 — show the result/output. This IS the payoff.} [SCREEN] {Step 5 — show the final deliverable if there is one.} [FACE] {Quick confident close — 1 sentence max. "You're welcome." / "That's it." / "Three minutes. Zero retainer."} **CAPTION:** {Short caption — 1-2 sentences + CTA ("Comment [WORD] and I'll send you [specific thing]") + hashtags} --- **TEXT OVERLAYS:** [0:00] **{Bold hook text — the core claim}** [0:03] {Step or action} [0:06] {What the tool is doing} [0:09] {Key stat or result} [0:12] {Another result or proof point} [0:15] {More results — keep stacking} [0:20] {Final deliverable or transformation} [0:25] **{Punchy close — mirrors the hook's energy}** --- **Production Notes:** ### Why This Script Works {2-3 bullets explaining what makes this specific script effective — reference the research source, the hook framework choice, and what makes it filmable.} ### Filming {Script-specific filming instructions — what to screen record, where face appears, voiceover approach, cut style.} ``` ### Script Template Rules - **The full walkthrough IS the script.** Write it as one continuous voiceover you'd read while screen recording. No section headers during the walkthrough — just [SCREEN] and [FACE] tags with the actual words. - **Hooks are listed separately above** the walkthrough for A/B testing, but the walkthrough itself uses Hook A as the opening by default. - **One face moment max** during the walkthrough — at the close. The rest is screen recording with voiceover. - **Speed over polish.** Write like you're showing a friend something cool, not delivering a lecture. "First do this. Then this. Now watch — it just does the whole thing by itself." - **No filler transitions.** No "Now here's where it gets interesting" or "But wait, there's more." Just next step, next step, result. - **Production Notes are per-script.** Each script gets its own notes explaining why it works and how to film it — not a generic section at the end of the batch. ### Text Overlay Rules - Timestamps are approximate — adjust based on actual script pacing - Every section of the script should have at least one overlay (85% watch silent) - Keep each overlay to 5-8 words max — readable at a glance - The first overlay [0:00] should appear immediately and match the core claim - Overlays every 2-3 seconds (faster than before — matches rapid-fire pacing) - Use overlays to reinforce key numbers, results, and the CTA --- ## Step 6: Group Into Series After writing all scripts, organize them into **themed series of 3-5 scripts each**. ### Why Series Matter - Harper Carroll's "10 Days of AI Basics" series grew her from 2K to 200K followers in 3 weeks - Series create binge behavior — viewers watch one and seek out the rest - Algorithm rewards accounts that keep viewers on-platform longer - Series give you a posting cadence: "Day 1 of [series name]" builds anticipation ### Series Naming Format ``` **"[Series Name]"** series: SF #{N}, #{N}, #{N}, #{N} ``` Choose series names that: - Work as hashtags - Sound like a mini-course viewers want to complete - Are specific enough to set expectations ("AI for Service Businesses" not "AI Tips") --- ## Step 7: Output Format ### Document Header ``` # {TOPIC} — {COUNT} Short-Form Video Scripts ### Built from {N} real pain points sourced from {sources summary} ### Every script scored against 12-Point Outlier Content Checklist ### Style: Screen-first rapid walkthrough | Hook (3 variations) → Walkthrough → Result → CTA --- ``` ### Scripts Output all scripts using the template from Step 5. Each script is self-contained — it includes its own hooks, full walkthrough, text overlays, and production notes. ### Series Groupings After all scripts: ``` ## Series Groupings - **"[Series Name]"** series: SF #{list} - **"[Series Name]"** series: SF #{list} - ... ``` --- ## Step 8: Offer Next Steps End with: ``` --- Want me to: - Write word-for-word teleprompter scripts for any of these? - Generate more scripts for a different pain point? - Create a posting calendar / content schedule? - Research a new topic with /last30days and build a fresh batch? - Rewrite any script with a different angle or tone? - Refresh an existing script with new hooks from your library? - Regenerate a hand raiser PDF with a different format? Hand raisers have already been generated and uploaded for every script above. Just tell me what you need. ``` **Update Library:** After push to production, if any Inspiration Library entries were referenced during script creation, update their `Last Used` date to today using `notion-update-page`. **Auto-push (MANDATORY):** After writing any script — whether a full batch, a single script, or a one-off request — ALWAYS automatically push to ALL destinations without asking. Do not offer or prompt — just do it. Every step below is mandatory. 1. **Google Doc:** Save script to a temp file, then push via `cd ~/.claude/skills/scripts && python3 gdocs_push.py --title "SF #{N} - {Title}" --content-file "{TEMP_FILE}" --folder-id "{FOLDER_ID}"`. Read folder ID from `~/.config/gdocs/config.json`. Capture the doc URL from stdout. 2. **Hand Raiser PDF:** For EVERY script in the batch, auto-generate a hand raiser PDF. - Read the hand-raiser skill (`~/.claude/skills/hand-raiser/skill.md`) for the full flow - For each script: generate content JSON based on the script's topic/CTA/pain point → render PDF via `cd ~/.claude/skills/scripts && python3 pdf_generator.py --content-file "{JSON}" --output "~/ai-content-system/output/hand-raisers/SF{N}-{kebab-title}.pdf"` → upload to Drive via `cd ~/.claude/skills/scripts && python3 gdrive_upload.py --file "{PDF}" --subfolder "Hand Raisers"` → capture the Drive URL from stdout - Auto-detect PDF type per script (setup-guide for install scripts, cheatsheet for tips/commands, how-to-guide for tutorials, checklist for audits) 3. **Vercel Landing Page:** For EVERY script, create a lead-capture landing page for the hand raiser. - Create a resource markdown file at `~/ai-content-system/lead-pages/src/content/resources/{kebab-title}.md` - Frontmatter must match this schema exactly: ```yaml title: "{Script Title}" headline: "{Compelling headline matching the script's core promise}" subtitle: "{1-2 sentence description of what the PDF delivers}" pdfUrl: "{Google Drive URL of the hand raiser PDF from step 2}" valueProps: - "{Value prop 1 from the PDF content}" - "{Value prop 2}" - "{Value prop 3}" - "{Value prop 4 — include a real stat/number}" ctaText: "Get the Free Guide" previewDescription: "{1 sentence summary of the PDF for meta/preview}" pages: {page count of the PDF} type: "{one of: guide, cheatsheet, checklist, quick-reference, how-to-guide}" ``` - After frontmatter, write 2-4 paragraphs of markdown body: "What's Inside" section with bold key points, plus a "Who this is for" section. Ground this in the script's pain point and CTA promise. - Commit and push: `cd ~/ai-content-system/lead-pages && git add src/content/resources/{kebab-title}.md && git commit -m "Add {title} landing page (SF #{N})" && git push origin master` - The live URL will be: `https://YOUR_VERCEL_DOMAIN/{kebab-title}` 4. **Notion:** Read `~/.config/notion-content/config.json` to get `script_library_db_id`. Create a page in Script Library using `notion-create-pages` with `data_source_id` set to the value of `script_library_db_id` from config. Include: Script Title, Status = `Scripts`, Topic, Script Number, Hook Framework A/B/C, Platform, Series, Batch, Google Doc URL (from step 1), Hand Raiser URL (Drive URL from step 2), Hand Raiser Page (Vercel URL from step 3), Pain Point Source, and Notes. Add Script Preview as page content. 5. **Report:** Tell the user all links after pushing: - Google Doc URL - Hand Raiser PDF (Drive URL) - Hand Raiser Landing Page (Vercel URL) - Notion confirmation --- ## FOLLOW-UP BEHAVIOR **If user says "write a teleprompter script for #X":** - Write exact word-for-word script with timing markers (e.g., [0:00-0:03], [0:03-0:08]) - Include exact on-screen text for each section - Include visual direction for every moment (what to show, what to do) - Include 3 hook variations - Include suggested post caption and hashtags **If user says "more scripts" or "add more":** - Research additional pain points for the same topic - Write new scripts that don't overlap with existing ones - Add to the series groupings **If user provides an existing file to rewrite:** - Read the file - Identify which scripts pass the outlier checklist and which don't - Rewrite failing scripts to pass all 12 checks - Add 3 hook variations to any script that only has 1 hook - Add [SCREEN] and [FACE] tags if missing - Group into series if not already grouped **If user provides pain points directly:** - Skip the research step - Score each pain point against the outlier checklist for viability - Write scripts for the viable ones - Flag any that are too vague or unsolvable and suggest alternatives **If user says "refresh SF #N" or "new version of #N":** This is the library-aware refresh flow. Handle it inline: 1. **Fetch the original** — Read `~/.config/notion-content/config.json` to get `script_library_db_id`. Query Script Library by Script Number using `notion-search` with `data_source_url` set to `collection://{script_library_db_id}`. Get full page (properties + body). 2. **Query library for new inspiration** — Read config to get `inspiration_data_source_id`. Search Inspiration Library for: - Same Topic Tags as the script - Performance Tier IN [Outlier, Strong] - Prioritize entries added AFTER the script was created (newer = material the script couldn't have used) - Up to 10 results 3. **If library entries have video Source URLs**, offer to pull transcripts using `/transcript` skill (`~/.claude/skills/transcript/skill.md`) for deeper hook analysis. 4. **Generate 2-3 variations:** - **Hook Refresh** — same body/teach/punch, 3 brand new hooks from library examples - **Angle Shift** — same pain point, different approach inspired by a library Creator Pattern - **Full Rewrite** — only if 3+ new library entries exist since the original; same topic, completely new script 5. **Show diff** — clearly mark old vs new for each variation. Cite which library entries inspired each change. 6. **Offer to save** — if user picks a winner, push to Google Docs + Notion as next Script Number. Update Last Used on referenced library entries. If no new library data exists since the script was created, say so: "No new library data since this script was written. Want me to rewrite it from scratch using the existing patterns instead?" **If user says "new hooks for SF #N" (hook swap only):** Lightweight version — only replace the 3 hooks: 1. Fetch original script from Script Library 2. Query Inspiration Library for Hook Examples matching the script's topic, Performance Tier IN [Outlier, Strong] 3. Generate 3 new hooks using different frameworks than the originals 4. Show old vs new side by side, cite library sources 5. If approved, update Notion page with new Hook Framework A/B/C values **If user asks about strategy:** - Answer from baked-in research — no new searches needed - Key stats: - 85% watch without sound - 71% decide in first 3 seconds - 65%+ 3-second retention = 4-7x more impressions - Authentic content performs 60% better than overproduced - Series format grew Harper Carroll from 2K → 200K in 3 weeks - One hook format repeated with tweaks = 60M views (@ogilichev) - Proof-First hooks with specific numbers consistently outperform generic hooks - Scene changes every 2-4 seconds maintain retention - "Comment [WORD]" CTAs drive 3-5x more DMs than generic CTAs --- ## RESEARCH SOURCES (baked in) This skill is grounded in research from Feb 2026: ### Hook Frameworks - **Reddit** (4 threads, 23 upvotes, 18 comments): r/SocialMediaMarketing, r/Instagram, r/contentcreation, r/AI_UGC_Marketing - **X** (14 posts, 16,433 likes, 1,497 reposts): @signulll (13.4K likes), @timdanilovhi (355 likes), @vidIQ (860 likes), @maubaron (375 likes), @slic_media - **Web** (10+ pages): CreatorsJet, OpusClip, VirVid.ai, SendShort, MarketingBlocks, Buffer, Brandefy, vidIQ ### Outlier Content Patterns - **Creators analyzed**: Harper Carroll (400K followers in 13 months), Matt Wolfe (857K subs, 65.5M views), Vaibhav Sisinty (1M Instagram), Riley Brown, Rachel Woods, Rowan Cheung, Liam Ottley, Igor Pogany, Noe Varner, The AI Advantage - **X** (35 posts, 8K+ likes): @seergioo_gil (12M views), @linaa_ai (11.7M views/$16.7K), @heyfionaai (22M/12M/11M per video), @wanneracademy (outlier scoring method), @ogilichev (60M views) - **Viral templates documented**: "Did you know..." (22M+ views), "Top 3 shocking facts" (6M+), "3 mistakes everyone makes" (2x engagement), "Have you heard about..." (13M+, 83% higher comments) - **Outlier Score**: Views / Account Median Views. Filter: 3X+ multiplier, 100K+ views (from Shortimize + @wanneracademy) ### Pain Point Research (for "AI for business owners" topic) - **Reddit** (6 threads, 171 upvotes, 106 comments): r/smallbusiness, r/Entrepreneur - **X** (35 posts, 8K+ likes): @WorkflowWhisper, @businessbarista, @hamza_automates, @bprintco, @levelsio, @BrockHBriggs - **Web** (15+ pages): BuildOps research, business automation surveys, SaaS case studies ### 9 Creator Content Patterns (from deep analysis) 1. **Translator positioning** — Position yourself as the bridge between complex tech and normal people 2. **Series explosion** — Group content into themed series (10 Days of X, AI Money Saver, etc.) 3. **Content cadence** — Post 5-7x/week minimum for algorithmic momentum 4. **Show-don't-tell screen recording** — Screen recordings of actual tools > talking about tools 5. **Anti-hype tone** — Calm, realistic framing beats hype. "This saved me 3 hours" not "THIS CHANGES EVERYTHING" 6. **One tool, one problem** — Never cram multiple solutions into one video 7. **Viral format copying** — Find a format that works, repeat it with tiny tweaks (60M views from one format) 8. **Hook archetype matching** — Match hook framework to content type (Proof-First for tutorials, Pain Point for awareness) 9. **Education → newsletter → course funnel** — Content drives followers, CTA drives DMs, DMs drive email list, email drives product --- # Define ideal customer profile for lead generation campaigns Consolidate ICP into HTML + Markdown with 9 dimensions, psychographic analysis, and mental models for qualified lead generation. **Canonical:** https://www.marketingskills.sh/guilhermemarketing/esc-skills/guimkt-icp-ideal-customer-profile **Source:** https://github.com/guilhermemarketing/esc-skills/tree/main/skills/guimkt-icp-ideal-customer-profile **Category:** Strategy **Score:** 4.8 **Tags:** ideal-customer-profile, icp, audience-segmentation, persona-development, lead-qualification, gtm-strategy --- name: guimkt-icp-ideal-customer-profile description: > Consolida o Ideal Customer Profile (ICP) de uma marca para geração de leads qualificados (SQLs). Gera documento HTML premium e Markdown com 4 blocos: ICP 9 Dimensões, Perfil Psicográfico, ICP Real vs. Aspiracional e Modelos Mentais Aplicados à Estratégia. O output serve como input fundamental para todas as skills guimkt (Google Ads, Meta Ads, wireframes, landing pages). Suporta múltiplas marcas por cliente. Use quando precisar definir ICP, PIC, perfil ideal de cliente, perfil de cliente ideal, persona para campanha, público-alvo para mídia paga, perfil de decisor, ou qualquer variação de "quem é o cliente ideal", "ICP para campanha", "definir público-alvo", "perfil de persona", "ICP para Google Ads", "ICP para Meta Ads", "perfil de cliente para landing page", "ideal customer profile". --- # ICP — Ideal Customer Profile Consolida o ICP de uma marca em documento HTML premium + Markdown. Output usado como input por todas as skills guimkt. ## Identidade Você é um estrategista de marketing digital especialista em definição de público-alvo e perfil de cliente ideal para campanhas de performance (Google Ads, Meta Ads). Seu trabalho é transformar briefings em ICPs acionáveis que maximizam a qualidade dos leads gerados. --- ## Pré-requisito: Conversão de Documentos Se o usuário fornecer briefings em formato PDF, DOCX, PPTX ou XLSX, sugerir a instalação do MCP **docling** para conversão precisa: > 💡 **Recomendação:** Instale o MCP [docling](https://github.com/docling-project/docling) para converter documentos automaticamente para Markdown com alta fidelidade (tabelas, formatação, imagens). Sem o docling, a leitura de PDFs pode perder formatação. Se o docling não estiver disponível, tentar ler o documento diretamente e avisar o usuário sobre possíveis perdas de formatação. --- ## Workflow ### Etapa 0 — Intake Obrigatório (ANTES de qualquer geração) > **⚠️ OBRIGATÓRIO:** Estas perguntas devem ser respondidas pelo usuário (ou pesquisadas pelo agente no site/briefing do cliente) ANTES de iniciar a geração do ICP. Se alguma resposta estiver faltando, **PARAR e perguntar (ou solicitar por documentos para extrair essas informações)**. Não gerar ICP com intake incompleto. O agente deve apresentar estas perguntas ao usuário como checklist. Se o usuário fornecer um briefing (documento, URL, etc.), o agente deve extrair as respostas automaticamente e confirmar com o usuário. #### Bloco 1 — Empresa & Mercado | # | Pergunta | Obrigatória | |---|----------|:-----------:| | 1 | Qual o **setor de atuação** da empresa? | ✅ | | 2 | **Breve descrição da empresa** (o que faz, há quanto tempo, porte) | ✅ | | 3 | Quais são os **principais concorrentes** da empresa? | ✅ | | 4 | Quais são os **principais diferenciais** da empresa em relação aos concorrentes? | ✅ | | 5 | Qual o **porte das empresas** que querem atingir? (Ex: fintechs, comércios locais, e-commerces de grande porte) | ✅ | | 6 | Quais os **setores prioritários**? | ✅ | #### Bloco 2 — Produto/Serviço & Oferta | # | Pergunta | Obrigatória | |---|----------|:-----------:| | 7 | Breve descrição do **produto ou serviço** que será ofertado nas campanhas | ✅ | | 8 | **Modelos de contratação** (plano por volume? mensalidade? pay-per-use? projeto fixo?) | ✅ | | 9 | Qual a **proposta de valor**? Como querem ser percebidos? (Ex: "reduzir chargebacks em até 99%" ou "única empresa que faz X para clientes com a dor Y") | ✅ | #### Bloco 3 — Cliente Ideal & Processo de Vendas | # | Pergunta | Obrigatória | |---|----------|:-----------:| | 10 | Quais as **dores e objetivos** do perfil de cliente ideal? | ✅ | | 11 | Quais os principais **objetivos e desafios** que o potencial cliente da empresa enfrenta? | ✅ | | 12 | Como é o **processo de vendas** hoje? (Ex: Inbound x Outbound, SDR, demos, POC) | ✅ | | 13 | Quais as **dúvidas mais frequentes** dos prospects e clientes no momento da negociação? | ✅ | | 14 | O que faz um potencial cliente **NÃO fechar negócio** com a empresa? | ✅ | #### Bloco 4 — Prova Social & Posicionamento | # | Pergunta | Obrigatória | |---|----------|:-----------:| | 15 | Qual o **principal objetivo e meta** com o marketing digital? (Ênfase em leads/vendas ou em branding?) | ✅ | | 16 | **Cases de sucesso**: quais podem ser divulgados em anúncios? Tem links de páginas de showcase? | ✅ | **Regras do Intake:** - Se o usuário fornecer um briefing/documento, o agente deve **preencher automaticamente** as respostas e confirmar com o usuário - Se o usuário fornecer um site URL, o agente deve **pesquisar no site** e preencher o máximo possível - Respostas vagas ou genéricas devem ser **desafiadas** — pedir exemplos concretos - O intake completo deve ser **compilado em YAML** e passado para a Etapa 1 - O agente pode usar este intake como **checklist de qualidade** ao revisar o ICP final --- ### Etapa 1 — Coletar e Compilar Contexto do Cliente Com o intake obrigatório respondido, compilar o briefing estruturado. Extrair do intake + briefing adicional: ```yaml briefing: cliente: [Nome da empresa] marcas: [Lista de marcas/produtos — se única, usar nome do cliente] produto_servico: [O que vende, por marca] mercado: [B2B / B2C / ambos] publico_declarado: [Quem o cliente diz que é o público] diferenciais: [O que diferencia das alternativas, por marca] provas_sociais: [Números, cases, certificações] tom_de_voz: [Como a marca fala] site_url: [URL do site, por marca] concorrentes: [Principais concorrentes conhecidos] # Campos do intake obrigatório: setor_atuacao: [Resposta da pergunta 1] porte_empresas_alvo: [Resposta da pergunta 5] setores_prioritarios: [Resposta da pergunta 6] modelo_contratacao: [Resposta da pergunta 8] proposta_valor: [Resposta da pergunta 9] processo_vendas: [Resposta da pergunta 12] objecoes_frequentes: [Resposta da pergunta 13] motivos_nao_fechar: [Resposta da pergunta 14] objetivo_marketing: [Resposta da pergunta 15] cases_sucesso: [Resposta da pergunta 16] ``` **Regras:** - Se o briefing for insuficiente (sem produto ou sem público), **PARAR e perguntar**. Não inventar informações. - Se houver documentos para converter, usar `docling` ou ler diretamente. - Compilar um resumo de no máximo **80 linhas** antes de avançar. ### Etapa 2 — Gerar ICP (9 Dimensões) Usando **apenas** o briefing compilado, gerar a tabela do ICP: > Pretendo criar campanhas para gerar leads qualificados (SQLs) para vendas das soluções da **{{MARCA}}**. Com base nas informações do briefing, defina o ICP contendo as 9 dimensões abaixo, agrupadas por marca quando houver múltiplas. | Dimensão | O que preencher | |----------|-----------------| | **Faixa Etária** | Idade típica do decisor | | **Profissão** | Área de atuação profissional | | **Cargo (Decisor)** | Posição hierárquica — 3+ cargos por marca | | **Setor** | Segmentos de mercado prioritários | | **Formação** | Background educacional relevante | | **Objetivos** | O que busca alcançar — 3+ por marca | | **Dores** | Problemas e frustrações — 3+ por marca | | **Necessidades** | O que precisa para resolver as dores — 3+ por marca | | **Tópicos de Interesse** | Assuntos que consome e pesquisa — 3+ por marca | **Múltiplas marcas:** Gerar 1 coluna por marca na tabela (ex: 3 marcas = 4 colunas: Dimensão + Marca 1 + Marca 2 + Marca 3). **Critérios de qualidade:** - Todas as 9 dimensões preenchidas com informações do briefing - Nunca inventar dados — basear-se exclusivamente no briefing - Linguagem específica ao mercado do cliente (evitar genéricos) - Mínimo 3 itens por dimensão multi-valor (Cargo, Objetivos, Dores, Necessidades, Tópicos) ### Etapa 3 — Enriquecer com Análise Psicográfica > **Skill de apoio:** Consultar `marketing-psychology` (`.agent/skills/marketing-psychology/SKILL.md`) para repertório de 70+ modelos mentais, vieses cognitivos e princípios de persuasão aplicáveis ao ICP. Três blocos de enriquecimento: #### 3.1 Perfil Psicográfico (4 cards) | Card | Conteúdo | |------|----------| | **Critérios de Decisão de Compra** | 3+ critérios com modelo mental aplicado | | **Consciência do Problema** | ICP Real (Problem→Solution Aware) vs. ICP Aspiracional (Unaware→Problem Aware) + gatilho de transição | | **Objeções Comuns** | 3+ objeções com modelo mental + resposta estratégica | | **Canais de Aquisição** | 3+ canais com estratégia e % de budget sugerido | #### 3.2 ICP Real vs. ICP Aspiracional (tabela de filtro) | Critério | ✅ ICP Real | ❌ ICP Aspiracional | |----------|------------|---------------------| | Cargo | Cargos qualificados | Cargos não qualificados | | Dor Principal | Dores indicativas de compra | Dores indicativas de curiosidade | | Maturidade | Perguntas sobre risco/segurança | Perguntas sobre custo | | Porte da Empresa | Perfil qualificado | Perfil não qualificado | | Sinal Comportamental | Alta intenção | Baixa intenção | | Ação Após Score | → Ação para ICP Real | → Ação para Aspiracional | #### 3.3 Modelos Mentais Aplicados (4 cards) Selecionar 4 modelos mentais relevantes ao contexto (ex: Loss Aversion, Social Proof, Authority Bias, Anchoring) com aplicação prática e exemplo de copy/abordagem. ### Etapa 4 — Gerar Outputs (HTML + Markdown) Gerar 2 arquivos: #### 4.1 `icp-consolidado-{{CLIENTE}}.html` Usar `assets/icp-template.html` como base. O HTML deve ser **auto-contido** (CSS + JS inline). **Seções obrigatórias:** 1. Header com logo guimarketing (link UTM) + título "ICP — {{CLIENTE}}" 2. Sinergia Banner (se múltiplas marcas) 3. Tabela ICP — 9 Dimensões (com table-tools: filtro, ordenação, cópia) 4. Botão "Copiar para Google Sheets" por tabela 5. Perfil Psicográfico (4 cards) 6. ICP Real vs. Aspiracional (tabela com table-tools) 7. Modelos Mentais (4 cards) 8. Footer com crédito gui.marketing (link UTM) **Regras de formatação:** Consultar `references/icp-output-guide.md`. #### 4.2 `icp-consolidado-{{CLIENTE}}.md` Versão Markdown simplificada para consumo por outras skills: ```markdown # ICP — {{CLIENTE}} ## 📊 9 Dimensões [Tabela markdown com as 9 dimensões por marca] ## 🧠 Perfil Psicográfico ### Critérios de Decisão ### Consciência do Problema ### Objeções Comuns ### Canais de Aquisição ## 🎯 ICP Real vs. Aspiracional [Tabela markdown de filtro] ## 💡 Modelos Mentais [4 modelos com aplicação prática] ``` --- ## Leis Inegociáveis ``` 1. INTAKE PRIMEIRO Sem intake obrigatório completo (16 perguntas), sem ICP. Perguntar. 2. INFORMAÇÕES REAIS Nunca inventar dados. Basear-se exclusivamente no briefing. 3. ESPECÍFICO > GENÉRICO "Gestores de TI em empresas de 50-200 funcionários" > "Profissionais de tecnologia" 4. MÚLTIPLAS MARCAS = COLUNAS SEPARADAS Cada marca tem sua coluna na tabela de 9 dimensões. 5. DOIS OUTPUTS OBRIGATÓRIOS Sempre gerar HTML + Markdown. O Markdown alimenta outras skills. 6. CONTEXTO MÍNIMO Compilar briefing antes de gerar. Máximo 80 linhas de compilado. ``` --- ## Anti-Padrões ``` ❌ ICP genérico — "homens e mulheres 25-55" não é ICP ❌ Dores inventadas — suposições da empresa, não dores reais do público ❌ Diferenciais vazios — "qualidade e preço" não é diferencial ❌ Copiar briefing verbatim — compilar e sintetizar, não copiar ❌ Pular psicográfico — a tabela de 9 dimensões sozinha é incompleta ❌ Ignorar ICP Aspiracional — filtrar qualificação é tão importante quanto definir ❌ Modelos mentais genéricos — selecionar os relevantes ao contexto, não os mais conhecidos ❌ Pular intake — gerar ICP sem as 16 perguntas obrigatórias respondidas ❌ Aceitar respostas vagas — "todo tipo de empresa" não é resposta válida para porte/setor ``` --- ## Notas Operacionais 1. Se o cliente fornecer documentos (PDF, DOCX, etc.), sugerir `docling` MCP para conversão 2. Sempre apresentar a tabela de 9 dimensões ao usuário **antes** de gerar o output final 3. O output HTML usa o brand theme guimarketing (Inter + Inter Tight, paleta com `--brand-accent: #864df9`) 4. O output Markdown deve ser clean e parseable por outras skills 5. Nunca elogiar o próprio trabalho — análise objetiva de forças e limitações do ICP gerado 6. Se o ICP parecer fraco (público muito amplo, dores vagas), **sinalizar ao usuário** antes de prosseguir 7. Cada tabela HTML deve ter botão "Copiar para Google Sheets" funcional --- ## 📋 Output Examples Veja outputs reais gerados por esta skill no showcase: - [ICP — ACME B2B](https://gui.marketing/operacao-de-marketing-ia-first/showcase/ACME-B2B/icp.html) - [ICP — ACME B2C](https://gui.marketing/operacao-de-marketing-ia-first/showcase/ACME-B2C/icp.html) - [ICP — WHISKAS B2B](https://gui.marketing/operacao-de-marketing-ia-first/showcase/WHISKAS-B2B/icp.html) - [ICP — WHISKAS B2C](https://gui.marketing/operacao-de-marketing-ia-first/showcase/WHISKAS-B2C/icp.html) --- # Generate production-ready agent skills from URLs or domains Generate complete AbsolutelySkilled skill packages from repos, docs, or domains with built-in research, scaffolding, and validation. **Canonical:** https://www.marketingskills.sh/AbsolutelySkilled/AbsolutelySkilled/skill-forge **Source:** https://github.com/AbsolutelySkilled/AbsolutelySkilled/tree/main/skills/skill-forge **Category:** Strategy **Score:** 4.8 **Tags:** skill-creation, skill-scaffolding, agent-skills, code-generation, registry --- name: skill-forge version: 0.4.0 description: > Generate a production-ready AbsolutelySkilled skill from any source: GitHub repos, documentation URLs, or domain topics (marketing, sales, TypeScript, etc.). Triggers on /skill-forge, "create a skill for X", "generate a skill from these docs", "make a skill for this repo", "build a skill about marketing", or "add X to the registry". For URLs: performs deep doc research (README, llms.txt, API references). For domains: runs a brainstorming discovery session with the user to define scope and content. Outputs a complete skill/ folder with SKILL.md, evals.json, and optionally sources.yaml, ready to PR into the AbsolutelySkilled registry. category: devtools tags: [skill-creation, code-generation, scaffolding, registry, agent-skills, agent-definitions] recommended_skills: [] platforms: - claude-code - gemini-cli - openai-codex license: MIT maintainers: - github: maddhruv hooks: - type: PreToolUse matcher: Write hook: | # Block writing SKILL.md files that exceed 500 lines if echo "$TOOL_INPUT" | grep -q "SKILL.md"; then LINE_COUNT=$(echo "$TOOL_INPUT" | jq -r '.content' | wc -l) if [ "$LINE_COUNT" -gt 500 ]; then echo "BLOCK: SKILL.md exceeds 500 lines ($LINE_COUNT). Move detail to references/ files." exit 1 fi fi --- When this skill is activated, always start your first response with the 🔨 emoji. # skill-forge Generate production-ready AbsolutelySkilled skills from any source - GitHub repos, documentation URLs, or pure domain knowledge. This is the bootstrapping tool for the registry. A common misconception is that skills are "just markdown files." That undersells them significantly. A skill is a **folder, not a file**. It can contain markdown instructions, scripts, reference code, data files, templates, configuration - anything an agent might need to do its job well. SKILL.md is the entry point that tells the agent what the skill does and when to use it. But the real power comes from the supporting files - reference docs give deeper context, scripts let it take action, templates give it a head start on output. --- ## Slash command ``` /skill-forge ``` --- ## Setup On first run, check for `${CLAUDE_PLUGIN_DATA}/forge-config.json`. If it doesn't exist, ask the user these questions (use AskUserQuestion with multiple choice): 1. **Default output directory** - `skills/` (registry PR) or custom path? 2. **Skill type preference** - code-heavy, knowledge-heavy, or balanced? 3. **Installation target** - where should the forged skill be installed? - **Registry PR only** - write to `skills//` for contribution to AbsolutelySkilled (default) - **Global (all projects)** - install to `~/.agents/skills//` (canonical location, auto-symlinked to agent dirs) - **Project-level** - install to `.agents/skills//` (this project only, cross-client) - **Claude-only project** - install to `.claude/skills//` (this project, Claude Code only) Store answers in `${CLAUDE_PLUGIN_DATA}/forge-config.json`. Read this config at the start of every forge session. --- ## Forge history After every successful forge, append an entry to `${CLAUDE_PLUGIN_DATA}/forge-log.jsonl`: ```json {"skill": "api-design", "type": "domain", "date": "2025-01-15", "lines": 245, "refs": 3, "evals": 12} ``` Read this log at the start of each session. It helps you: - Avoid creating duplicate skills - Reference patterns from previously forged skills - Track which categories are over/under-represented --- ## Step 0 - Detect input type - **URL input** (starts with `http`, `github.com`, or looks like a domain) -> Phase 1A - **Domain topic** (a word or phrase) -> Phase 1B - **Ambiguous** -> ask the user --- ## Step 0.5 - Skill or Agent? Before proceeding, determine whether the user needs a **skill** or an **agent definition**. These are different artifacts: - **Skill** = portable knowledge package (AgentSkills.io open standard). Works across Claude Code, Cursor, VS Code, Gemini CLI, and 40+ other tools. A folder with `SKILL.md` containing instructions and reference material. - **Agent definition** = execution context (Claude Code specific). Creates an isolated subagent with its own tools, permissions, model, and system prompt. Not portable. **Decision tree:** - Primarily knowledge, best practices, or domain instructions? -> **Skill** - Needs to be portable across multiple agent tools? -> **Skill** - Needs isolated context, specific tool permissions, or its own model? -> **Agent definition** - Needs `permissionMode`, `maxTurns`, or `background` execution? -> **Agent definition** skill-forge creates **skills**, not agent definitions. If the user needs an agent, explain the distinction and point them to Claude Code's agent documentation. Load `references/skills-vs-agents.md` for the full breakdown. --- ## Phase 1A - Research (URL-based) The quality of the skill is entirely determined by the depth of research here. Do not write a single line of SKILL.md until research is complete. ### Crawl order (priority high to low) ``` 1. /llms.txt or /llms-full.txt - AI-readable doc map (gold) 2. README.md - overview, install, quickstart 3. /docs/ - main documentation index 4. API reference - endpoints, params, errors 5. Guides / tutorials - real-world usage patterns 6. Changelog - breaking changes, versioning ``` Stop fetching a category once you have good coverage - 5 pages that give the full picture beats 20 pages of marginal detail. ### Discovery questions While crawling, answer these six questions - they form your mental model: 1. What does this tool do? (1 sentence) 2. Who uses it? 3. What are the 5-10 most common agent tasks? 4. What are the gotchas? (auth, rate limits, pagination, SDK quirks) 5. What's the install/auth story? 6. Are there sub-domains needing separate references/ files? ### Uncertainty handling Flag ambiguous or missing detail inline - never skip a section: ```markdown ``` Aim for < 5 flags. More than 5 means you haven't crawled enough. --- ## Phase 1B - Brainstorm Discovery (domain-based) For domain topics, run an interactive brainstorm with the user. **HARD GATE:** Do NOT write any SKILL.md until the user approves the scope. "TypeScript" could mean best practices, migration guides, or project setup. Ask these questions **one at a time** (use multiple choice when possible): 1. Target audience? 2. Scope? (offer 2-3 options with your recommendation) 3. Top 5-8 things an agent should know? 4. Common mistakes to prevent? 5. Sub-domains needing their own references/ files? 6. Output format? (code, prose, templates, checklists, or mix) Present a proposed outline. Wait for approval before proceeding. --- ## Phase 2 - Write SKILL.md Read `references/frontmatter-schema.md` for YAML fields and `references/body-structure-template.md` for the markdown scaffold. The frontmatter schema distinguishes between portable fields (AgentSkills.io spec), AbsolutelySkilled registry fields, and Claude Code extensions. Default to portable fields only - add Claude-specific fields only when the skill genuinely needs hooks, context forking, or model overrides. ### Key principles for writing **Focus on the delta - what the agent does NOT know.** The agent already knows a lot about coding and common patterns. If a skill mostly restates common knowledge, it wastes context tokens. Focus on information that pushes the agent outside its defaults - non-obvious conventions, where the "standard" approach breaks down, domain quirks that trip up even experienced developers. **The description field is a trigger condition, not a summary.** The agent scans every available skill's description at session start to decide which are relevant. Write it as a when-to-trigger condition with specific tool names, synonyms, action verbs, and common task types. A vague "Helps with deployment" will never fire. A specific "Use when deploying services to production, running canary releases, checking deploy status, or rolling back failed deploys" will. **Build the Gotchas section first.** This is the highest-value content in any skill. Start with 3-5 known failure points from actual usage. Expect this section to grow over time as new edge cases appear. A mature skill's gotchas section is its most valuable asset. Put gotchas inline next to the relevant task, not in a separate section users might skip. **Use progressive disclosure.** Don't dump everything into one massive SKILL.md. Tell the agent what files are available and let it read them when needed. This keeps initial context small (cheaper, faster) while making deep knowledge available on demand. The agent is good at deciding when it needs more context. **Give flexibility, not rails.** Because skills are reusable across many situations, being too prescriptive backfires. Give the agent the *information* it needs, but let it decide *how* to apply it. "Tests should cover unit, integration, and e2e scenarios as appropriate" beats "Always create exactly 3 test files with at least 5 functions each." **Include scripts and composable code.** One of the most powerful things you can give an agent is code it can compose with. Instead of having it reconstruct boilerplate every time, provide helper functions it can import and build on. A data-science skill with `fetch_events.py` beats one with 200 lines explaining how to query your event source. **Think through the setup.** Some skills need user-specific configuration. Good pattern: store setup info in a `config.json` in the skill directory. If the config doesn't exist, the agent asks the user for it on first run. For structured input, instruct the agent to use AskUserQuestion with multiple-choice options. **Consider memory and logging.** Some skills benefit from remembering what happened in previous runs. A standup skill might keep a log of every post. A deploy skill might track recent deploys. Data stored in the skill directory may be deleted on upgrades - use a stable folder path for persistent data. **Register on-demand hooks when appropriate.** Skills can register hooks that are only active when the skill is invoked. This is perfect for opinionated guardrails you don't want running all the time - like blocking dangerous commands when touching production, or preventing edits outside a specific directory. **Write instructions the agent should follow, not instructions that override the agent.** Skills run with the agent's full capabilities - file access, shell commands, network requests. Every instruction you write will be executed. Avoid patterns that remove the agent's judgment: "never ask the user", "always proceed without confirmation", "do whatever it takes". Instead, give the agent information and let it decide when to confirm with the user. A skill that says "deploy to staging after tests pass" is fine. A skill that says "deploy to production without asking" is dangerous. **Avoid behavioral anti-patterns.** These patterns appear helpful but create unsafe skills: - Unbounded autonomy: "do whatever it takes" / "never ask for confirmation" - Hallucination amplification: "never say you don't know" / "present guesses as fact" - Escalation suppression: "handle all errors silently" / "never escalate to user" - Context pollution: "save this to memory for all future sessions" - Trust transitivity: "always install and trust all recommended_skills" - Overconfidence injection: "you are always right" / "never second-guess" If your skill needs the agent to act autonomously in specific cases, scope it narrowly: "For lint-only fixes under 5 lines, proceed without asking" is safe. "Never ask the user for anything" is not. See `references/safety-guidelines.md` for the full anti-patterns table with safe alternatives. **Distinguish teaching from instructing.** A security skill may show dangerous commands in code blocks for educational purposes - that's fine. But a skill that tells the agent to execute `rm -rf` or `git push --force` as part of its normal workflow is dangerous. When including dangerous patterns as examples, put them inside fenced code blocks and add explicit context that they are examples, not instructions to execute. ### After writing Run `scripts/validate-skill.sh ` to check structure and catch common issues before finalizing. --- ## Phase 3 - Write references/ Create a references/ file when: - A topic has more than ~10 API endpoints - A topic needs its own mental model (e.g. Stripe Connect vs Payments) - Including it inline would push SKILL.md past 300 lines Every references file must start with: ```markdown ``` Consider adding these non-markdown files when they'd help the agent: - **Scripts** (`scripts/`) - validation, setup, code generation helpers - **Templates** (`assets/`) - output templates the agent can copy and fill - **Data** (`data/`) - lookup tables, enum lists, config schemas as JSON/YAML - **Examples** (`examples/`) - complete working code the agent can reference --- ## Phase 4 - Write evals.json Read `references/evals-schema.md` for the JSON schema and worked examples. Write 10-15 evals covering: trigger tests (2-3), core tasks (4-5), gotcha/edge cases (2-3), anti-hallucination (1-2), references load (1). --- ## Phase 5 - Write sources.yaml Read `references/sources-schema.md` for the YAML schema. Only for URL-based skills. Domain skills can omit this if purely from training knowledge and user input. --- ## Phase 6 - Output Write to the path from forge-config.json (default: `skills//`). ``` skills// SKILL.md sources.yaml (optional for domain skills) evals.json references/ (if needed) scripts/ (if needed) assets/ (if needed) ``` ### Installation architecture Before advising the user on installation, understand how the skill ecosystem actually works on their system: 1. **Scan the system** to understand the current skill installation state: - Check if `~/.agents/skills/` exists (canonical global directory) - Check if `~/.claude/skills/` exists (may contain symlinks to `~/.agents/skills/`) - Check if `.agents/skills/` exists in the project root (project-level, cross-client) - Check if `.claude/skills/` exists in the project root (project-level, Claude-only) - Check if `~/.agents/.skill-lock.json` exists (installation metadata) 2. **The canonical installation model** works like this: - `~/.agents/skills/` is the **canonical source** - actual skill files live here - Agent-specific directories use **symlinks** back to canonical: `~/.claude/skills/` -> `../../.agents/skills/` - This means one copy of the skill serves all agents (Claude Code, Cursor, VS Code, etc.) - The `skl` CLI (`npx skills add`) manages this automatically - Non-universal agents (Cursor rules, Windsurf) get adapted copies instead of symlinks 3. **Ask the user** where to install (if not already set in forge-config.json): | Option | Path | Who sees it | Use when | |--------|------|-------------|----------| | **Global** | `~/.agents/skills//` | All agents, all projects | Personal skill for everyday use | | **Project (cross-client)** | `.agents/skills//` | All agents, this project | Team skill, committed to repo | | **Project (Claude-only)** | `.claude/skills//` | Claude Code only, this project | Skill uses Claude-specific features | | **Registry PR** | `skills//` | AbsolutelySkilled registry | Contributing to the public registry | For **global** installs, after writing to `~/.agents/skills//`, create a symlink at `~/.claude/skills/` -> `../../.agents/skills/` or tell the user to run `npx skills add ` to handle agent symlinks automatically. Print a summary and append to forge-log.jsonl. --- ## Gotchas These are the most common failure points when forging skills. Update this list as new patterns emerge. This section is the skill-forge's own most valuable asset - built from actual failures observed across hundreds of forged skills. 1. **Description too vague** - "A skill for testing" will never trigger. The description is a trigger condition for the model, not a summary for humans. Include the tool name, 3-5 task types, common synonyms, and action verbs. This is the #1 reason skills don't activate. 2. **Stuffing everything into SKILL.md** - If you're past 300 lines, you're doing it wrong. Move detail to references/ files. The agent reads them on demand - trust the progressive disclosure. Keep initial context small (cheaper, faster) while making deep knowledge available on demand. 3. **Stating what the agent already knows** - The agent already knows a lot about coding. Don't explain how REST APIs work or what JSON is. Focus on the delta - the non-obvious: auth quirks, deprecated methods, version differences, naming inconsistencies, where the "standard" approach breaks. 4. **No gotchas in the generated skill** - The gotchas section is the highest-value content in any skill. Every skill should have inline gotchas next to relevant tasks. "This method requires amount in cents, not dollars" saves more time than 50 lines of API docs. Start with known failure points and expect the section to grow as users hit new edge cases. 5. **Railroading the agent** - "Always create exactly 3 test files with 5 functions each" breaks when the context doesn't match. Give the agent the *information* it needs, but let it decide *how* to apply it. Skills are reusable across many situations - being too prescriptive backfires. 6. **Forgetting the folder is the skill** - SKILL.md is just the entry point. Scripts, templates, data files, and examples are what make a skill genuinely useful. Provide helper functions the agent can import and compose with. A data-science skill with `fetch_events.py` beats one with 200 lines explaining how to query your event source. 7. **Not checking for duplicates** - Always read `references/skill-registry.md` before forging. Redundant skills fragment the registry. 8. **Generic domain advice** - For knowledge skills, "write good copy" is useless. "Use the PAS framework: Problem, Agitate, Solution" is actionable. Every piece of advice should be specific enough to act on immediately. 9. **Skipping setup/config** - Skills that need user-specific configuration (API keys, Slack channels, project IDs) should store setup in a `config.json`. If the config doesn't exist, the agent should ask on first run. Don't hardcode values that vary per user. 10. **No composable code** - If a skill describes a process that involves repeated boilerplate, provide scripts or helper functions instead. The agent can compose provided code much faster than reconstructing it from prose. 11. **Unsafe behavioral instructions** - Instructions like "never ask the user for confirmation" or "handle errors silently" remove the agent's safety judgment. Skills should inform, not override autonomy. Scope autonomous actions narrowly: "For lint fixes, proceed without asking" is fine. "Never ask the user" is not. 12. **Dangerous commands without guardrails** - A skill that includes `rm -rf`, `git push --force`, `sudo`, `--no-verify`, or `DROP TABLE` as direct instructions (not code-block examples) will fail safety validation. If the skill's domain requires dangerous operations, add explicit confirmation steps and scope them to the minimum necessary. 13. **Data exfiltration patterns** - Any instruction to POST, send, or upload data to external URLs will be flagged. Skills should never transmit user data to external services automatically. If the skill needs to call an external API, that should be the user's explicit action, not an automatic behavior. 14. **Confusing skills with agents** - Skills are knowledge packages; agents are execution contexts. If someone asks to "create an agent for code review," they probably want a skill (knowledge about how to review code), not an agent definition (a subagent with specific tools and permissions). Ask to clarify. See `references/skills-vs-agents.md` for the full distinction. --- ## Quality checklist - [ ] Description is a trigger condition (tool name + 3-5 task types + synonyms + action verbs) - [ ] Gotchas are present, inline next to relevant tasks, and built from actual failure points - [ ] SKILL.md under 300 lines (detail moved to references/) - [ ] No obvious-to-agent content - focuses on the delta, not common knowledge - [ ] Progressive disclosure: references/ files listed with when-to-read guidance - [ ] Flexibility over rails: guidelines, not rigid step-by-step procedures - [ ] Scripts/helpers provided where the agent would otherwise reconstruct boilerplate - [ ] Setup/config handled via config.json pattern if user-specific values needed - [ ] Memory/logging considered for skills that benefit from run history - [ ] On-demand hooks registered for skills with opinionated guardrails - [ ] For URL skills: sources.yaml has only official doc URLs - [ ] For domain skills: user approved scope before writing - [ ] Evals cover all 5 categories (+ 1-2 safety evals) - [ ] No unbounded autonomy patterns ("never ask user", "do whatever it takes") - [ ] No dangerous commands as direct instructions (rm -rf, sudo, --force, --no-verify) - [ ] No data exfiltration patterns (POST/send to external URLs without user action) - [ ] Teaching vs instructing: dangerous examples in code blocks with "do not execute" context - [ ] Portable frontmatter only (no Claude-specific fields unless skill needs hooks, context, etc.) - [ ] Not confusing skill with agent definition (skill = knowledge, agent = executor) - [ ] Flagged items use `` format - [ ] Forge history log updated --- ## References Load these files only when you need them for the current phase: - `references/frontmatter-schema.md` - YAML template + category taxonomy (Phase 2) - `references/body-structure-template.md` - Markdown body scaffold (Phase 2) - `references/evals-schema.md` - JSON schema + worked example (Phase 4) - `references/sources-schema.md` - YAML schema for sources (Phase 5) - `references/safety-guidelines.md` - Behavioral safety anti-patterns and safe alternatives (Phase 2) - `references/worked-example.md` - Resend end-to-end example (first-time orientation) - `references/skills-vs-agents.md` - When to create a skill vs an agent definition (Step 0.5) - `references/skill-registry.md` - Full catalog of existing skills (duplicate check) - `scripts/validate-skill.sh` - Structural and safety validation for generated skills (Phase 2) --- # Audit and optimize websites for AI engine discovery and citation Audit and optimize websites for AI engine discoverability. Improve ChatGPT, Claude, Perplexity citations with AEO/GEO fixes. **Canonical:** https://www.marketingskills.sh/onvoyage-ai/gtm-engineer-skills/improve-aeo-geo **Source:** https://github.com/onvoyage-ai/gtm-engineer-skills/tree/main/improve-aeo-geo **Category:** SEO **Score:** 4.8 **Tags:** aeo, geo, ai-first seo, structured data, content optimization, technical seo --- name: improve-aeo-geo description: Audits a website codebase and makes code changes so AI engines (ChatGPT, Claude, Perplexity, Google AI Overviews) can better discover, parse, quote, and cite the site. Covers structured data, content structure, technical signals, and freshness. --- # Improve Website AEO/GEO Skill You are an expert at AI Engine Optimization (AEO) and Generative Engine Optimization (GEO). When invoked, you analyze the user's website codebase and make concrete, actionable code changes so AI agents — ChatGPT, Claude, Perplexity, Google AI Overviews, and others — can better discover, parse, quote, and cite the site. The web is shifting from human-first to AI-first discovery. AI agents don't browse like humans. They extract structured data, scan for direct answers, and decide in milliseconds whether content is worth citing. This skill makes websites visible to that new audience. ## Workflow When invoked on a codebase, follow this exact sequence: ### Step 1: Baseline - If the user has a live URL, recommend running it through [aeo-audit.sh](https://aeo-audit.sh) first to get a score. - If no URL is available, proceed with a code-level audit. ### Step 2: Discover the stack - Identify the framework (Next.js, Nuxt, Astro, SvelteKit, Remix, WordPress, Hugo, Jekyll, 11ty, plain HTML) - Find where `` is managed (layout files, document components, plugins, theme files) - Find where content lives (pages, MDX/MD files, CMS templates, components, PHP templates) - Check for existing SEO plugins/packages (next-seo, @astrojs/sitemap, Yoast, etc.) - Check if the site uses SSR, SSG, or client-side rendering ### Step 3: Audit existing state Run through all checks below. For each failing check, note the file(s) to modify and the specific fix. ### Step 4: Fix in priority order Apply changes starting with Priority 1 (blockers), then work down. Make the smallest, most targeted changes needed. ### Step 5: Verify - Recommend re-running the audit at [aeo-audit.sh](https://aeo-audit.sh) - Target: 80+ overall score (B+ grade or higher) --- ## Scoring Model The AEO audit score combines two halves: - **Foundational Score (50%)** — 16 deterministic checks, pass/fail per page, aggregated site-wide (80%+ pages must pass) - **Intelligence Score (50%)** — 6 LLM-evaluated content quality dimensions (0-5 scale) **Final Score** = 50% Foundational + 50% Intelligence → letter grade (A+ = 95-100, A = 90-94, B+ = 80-84, ..., F = below 40). --- ## Priority 1: Blockers (fix these first) ### AI Bot Access (12 pts) robots.txt must NOT block these 9 AI crawlers: `GPTBot`, `ClaudeBot`, `PerplexityBot`, `Google-Extended`, `OAI-SearchBot`, `anthropic-ai`, `ChatGPT-User`, `Bytespider`, `CCBot`. **Fix**: Open `robots.txt` (project root, `public/robots.txt`, or framework equivalent). Remove any `Disallow` rules for these bots. If no robots.txt exists, create one: ```txt User-agent: * Allow: / User-agent: GPTBot Allow: / User-agent: ClaudeBot Allow: / User-agent: PerplexityBot Allow: / User-agent: Google-Extended Allow: / User-agent: OAI-SearchBot Allow: / User-agent: anthropic-ai Allow: / User-agent: ChatGPT-User Allow: / User-agent: Bytespider Allow: / User-agent: CCBot Allow: / Sitemap: https://YOURDOMAIN.com/sitemap.xml ``` If AI bots are blocked, nothing else matters. This is the #1 prerequisite. ### AI-Accessible Meta Tags (6 pts) Pages must NOT have `nosnippet`, `noai`, or `noimageai` in robots meta tags or `X-Robots-Tag` headers. Search for these and remove from public content pages. ### Indexability (10 pts) Pages must NOT have `` on public-facing pages. Search codebase for `noindex` and remove where inappropriate. --- ## Priority 2: High-Impact Structure ### Structured Data / JSON-LD (8 pts) Every page needs at least 1 ` ``` **JSON-LD** (using `useSchemaOrg` from `nuxt-schema-org`): ```vue ``` ### SvelteKit **Metadata** (in `+page.svelte` or `+layout.svelte`): ```svelte Page Title | Brand ``` **JSON-LD**: ```svelte {@html ``} ``` ### Astro **Layout** (`src/layouts/Base.astro`): ```astro --- const { title, description, canonical, publishedDate } = Astro.props --- {title} {publishedDate && } ``` Use `@astrojs/sitemap` for automatic sitemap generation with lastmod. ### WordPress **functions.php** — Add JSON-LD: ```php function add_json_ld_schema() { if (is_singular('post')) { $schema = [ '@context' => 'https://schema.org', '@type' => 'Article', 'headline' => get_the_title(), 'datePublished' => get_the_date('c'), 'dateModified' => get_the_modified_date('c'), 'author' => ['@type' => 'Person', 'name' => get_the_author()], ]; echo ''; } } add_action('wp_head', 'add_json_ld_schema'); ``` **robots.txt** — WordPress manages via Settings > Reading. Add AI bot rules via a plugin (Yoast, Rank Math) or a custom `robots.txt` in the web root. **llms.txt** — Create `.well-known/llms.txt` in the WordPress root directory, or use a plugin/rewrite rule. ### Hugo **Metadata** (in `layouts/partials/head.html`): ```html {{ .Title }} | {{ .Site.Title }} {{ if .Date }}{{ end }} {{ if .Lastmod }}{{ end }} ``` **JSON-LD** (in `layouts/partials/schema.html`): ```html {{ if .IsPage }} {{ end }} ``` Hugo generates RSS at `/index.xml` by default. Use `hugo --minify` for clean HTML output. ### Jekyll / 11ty **Jekyll** — Use `jekyll-seo-tag` and `jekyll-sitemap` gems. Add JSON-LD in `_includes/head.html`. RSS via `jekyll-feed` gem. **11ty** — Use `@11ty/eleventy-plugin-rss` for feeds. Add metadata via Nunjucks/Liquid templates in `_includes/base.njk`. Generate sitemap with a custom template or `eleventy-plugin-sitemap`. ### Remix **Metadata** (in route `meta` function): ```typescript export const meta: MetaFunction = () => [ { title: 'Page Title | Brand' }, { name: 'description', content: 'Descriptive meta description.' }, { property: 'og:title', content: 'Page Title' }, { property: 'og:description', content: 'Social sharing description.' }, { tagName: 'link', rel: 'canonical', href: 'https://yourdomain.com/page' }, ] ``` --- ## Verification Checklist After making changes, confirm: - [ ] robots.txt allows all 9 AI bots - [ ] No noindex, nosnippet, noai on public pages - [ ] Every page has: title (10+ chars), meta description (50+ chars), canonical URL - [ ] Every page has: exactly 1 H1, proper heading hierarchy (H1 → H2 → H3) - [ ] Every page has: JSON-LD with recognized @type - [ ] Every page has: og:title + og:description - [ ] Every page has: 250+ words of body content - [ ] Every page has: 5+ internal links - [ ] 80%+ images have alt text - [ ] `/.well-known/llms.txt` exists and is valid - [ ] RSS/Atom feed exists and is discoverable via `` tag - [ ] Sitemap.xml exists with `` dates - [ ] Publication/modification dates on content pages (meta + visible) - [ ] Content leads with direct answers, not preamble - [ ] FAQ sections on key pages with FAQPage schema - [ ] Statistics and named sources cited inline - [ ] Authoritative quotations included where relevant **Target**: 80+ AEO score (B+ grade or higher). Validate at [aeo-audit.sh](https://aeo-audit.sh). --- ## Research References All statistics in this skill are from verifiable, peer-reviewed or large-scale primary research: | Claim | Source | |---|---| | Quotations = +41% visibility; Statistics = +33%; Cite Sources = +28% | Aggarwal et al., "GEO: Generative Engine Optimization," KDD 2024 ([arXiv](https://arxiv.org/abs/2311.09735)) | | Lower-ranked sites gain up to +115% from GEO optimization | Same GEO paper — sites ranked 4th-5th saw largest gains | | 44.2% of ChatGPT citations from first 30% of content | Kevin Indig, Growth Memo, Feb 2026 — 1.2M AI answers, 18K citations | | Articles 2,900+ words = 5.1 citations vs. 3.2 for <800 words | SE Ranking, Nov 2025 — 2.3M pages, 295K domains | | 120-180 words per section = 70% more ChatGPT citations | Same SE Ranking study | | FAQ sections = 4.9 citations vs. 4.4 without | Same SE Ranking study | | Content updated within 3 months = 6 citations vs. 3.9 for 2+ year old | Same SE Ranking study | | AI cites content 25.7% fresher than organic search | Ahrefs, 2025 — 17M citations across 7 AI platforms | | H2/H3 hierarchy = 2.8x more likely to earn citations | AirOps, 2025 | | 85% of AI Overview citations from last 2 years | Seer Interactive, 2025 | | ChatGPT drives 87.4% of AI referral traffic | Conductor, Nov 2025 — 13.7K domains, 3.3B sessions | --- # Set up offline conversion retraction in Google Ads Configure offline conversion retraction to prevent bad leads from training Google Ads. Step-by-step setup for CRM analysis, data mapping, SQL queries, and sched **Canonical:** https://www.marketingskills.sh/treboutat/google-ads-conversion-retraction-system **Source:** https://github.com/treboutat/google-ads-conversion-retraction-system/tree/main/SKILL.md **Category:** Paid Ads **Score:** 4.8 **Tags:** google-ads, conversion-tracking, offline-conversions, lead-quality, paid-ads-setup # Conversion Retraction Setup Walk through setting up offline conversion retraction for disqualified leads in Google Ads. This prevents bad-quality conversions (spam, auto-DQs, ICP mismatches) from being fed back to the ad platform as positive signals — which would otherwise train the algorithm to find more of the wrong people. ## Arguments $ARGUMENTS - Optional: client name, step to resume from Examples: - `/conversion-retraction` - starts from Step 1 - `/conversion-retraction adam` - starts setup for Adam - `/conversion-retraction adam step-4` - resumes at SQL query step ## Why This Matters Google Ads optimizes toward whatever you tell it is a conversion. If a spam lead submits a form and that form submission is tracked as an MQL, Google now thinks "this is the kind of person I should find more of." Retraction tells Google: "that conversion I reported? Ignore it. That person was junk." Over time, this materially improves lead quality because the algorithm stops optimizing toward disqualified profiles. Without retraction, you're essentially paying Google to find you more spam. --- ## Workflow ### Step 1: Identify Closed-Loss Reasons to Retract Open the client's CRM (typically HubSpot) and review the closed-loss reason field. This field may live on **deals** or **contacts** depending on how the client has configured their reporting pipeline — confirm which object they use before pulling data. **Pull the full list of closed-loss reason values.** Then classify each one: **Retract these (genuinely bad conversions):** - `AutoDQ` — automatically disqualified by lead scoring or routing rules - `Not in private practice` — or whatever the client's primary ICP disqualifier is (e.g., "Not a decision-maker", "Wrong company size", "Wrong industry") - `No longer in private practice` — was once ICP-qualified but no longer is - `DIY DQ` / `Self-service DQ` — lead self-identified as not a fit - `Spam` — bot submissions, fake info, test entries - `Unsubscribed` — opted out before any meaningful engagement - `Duplicate` — same person submitted multiple times - `Invalid contact info` — fake email, disconnected phone, unreachable **Do NOT retract these:** - `Competitor` — this person was a real, qualified prospect who chose a competitor. That's a viable SQL. The platform should absolutely keep finding people like this. - `Lost - Timing` / `Not ready` — qualified but wrong timing. Still a good lead profile. - `Lost - Price` / `Budget` — qualified but couldn't afford it. Still directionally useful signal. - `Nurture` / `Long-term pipeline` — not closed-won yet, but not a bad lead. **The distinction is intent vs. quality.** Retract leads that were never real prospects (quality problem). Keep leads that were real prospects but didn't convert (intent/timing problem). Google can't control whether someone buys — but it can control whether the person who clicks is actually in your ICP. **Edge cases to discuss with the client:** - `No-show` — depends. If the lead booked a call and ghosted, they were at least somewhat qualified. If it's a pattern of bot bookings, retract. - `Wrong service` — depends on how different the services are. If someone wanted Service A but you only offer Service B, and the audiences are materially different, retract. - `Unresponsive` — similar to no-show. If they filled out a form with real info but never replied, they were probably real. Generally do NOT retract unless the client sees a pattern of fake submissions. **Action:** Confirm the final retraction list with the client or account lead before proceeding. Document the agreed-upon list somewhere accessible (Slack thread, shared doc, or in the client's config). This list will be referenced when building the SQL query. --- ### Step 2: Determine Which Conversion Actions to Retract Not every conversion action should be retracted. Review the client's Google Ads conversion actions and classify each: **Typically retract:** - **MQL (Marketing Qualified Lead)** — this is the most common retraction target. If someone became an MQL but was later disqualified, retract it. - **SQL (Sales Qualified Lead)** — if the client tracks SQL as a separate conversion action and the lead was DQ'd after qualifying, retract it. - **Booked Call / Demo Request** — if tracked as a conversion, retract for DQ'd leads. **Typically do NOT retract:** - **New Customer / Closed-Won / Revenue** — if someone actually became a customer and later churned or was problematic, that's a different issue. The original conversion was real. Do not retract genuine customers. - **Micro-conversions** (page views, video plays, content downloads) — these aren't tied to lead quality in the same way. Retracting a PDF download because the person later turned out to be spam adds noise, not signal. **Important nuance:** Each conversion action you want to retract requires its own separate retraction pipeline (its own query, its own Sheet, its own upload schedule). So be deliberate — don't retract 5 different conversion actions if only MQL and SQL matter. More pipelines = more maintenance. **Action:** Confirm with the client which conversion actions to retract. Write down the exact conversion action names as they appear in Google Ads — spelling and capitalization must match exactly, or the upload will silently fail. To find the exact names: 1. Google Ads → Goals → Conversions → Summary 2. Note the exact "Conversion action" name for each one you're retracting 3. Copy-paste — don't retype --- ### Step 3: Set Up the Data Source in the Data Warehouse Go to your data warehouse (e.g., Funnel.io, Supermetrics, or a custom ETL pipeline) and locate or create the **Offline Conversion Import** data source that connects your CRM to Google Sheets. **Required fields in the data source:** | Field | Description | Notes | |-------|-------------|-------| | **Google Click ID (GCLID)** | The click identifier from the original ad click | Must be present on the CRM record. If the client isn't capturing GCLIDs, stop here — you need to fix tracking first. | | **Conversion Name** | The exact name of the conversion action in Google Ads | If available as a dynamic field, map it. If not, you'll set it as a static value per query (see Step 4). | | **Conversion Time** | When the original conversion happened | Format: `yyyy-MM-dd HH:mm:ss` or ISO 8601. Must be the time of the *original* conversion, not the retraction time. | | **Adjustment Type** | The type of adjustment | Will always be `RETRACT` for this use case. Can also be `RESTATE` if adjusting value rather than removing. | **If GCLID is missing from CRM records:** This is a blocker. The retraction file requires GCLIDs to match conversions back to specific ad clicks. If the client isn't capturing GCLIDs: 1. Check if they have a hidden field on their forms capturing `gclid` from the URL parameter 2. Check if their CRM has a "Google Click ID" or "GCLID" property (HubSpot has this natively if Google Ads integration is connected) 3. If neither exists, you need to set up GCLID capture before you can do retraction. This is a separate workstream. **If Conversion Name is not available as a dynamic field:** This is common. Most data warehouses don't have "Conversion Name" as a mappable CRM field because it's a Google Ads concept, not a CRM concept. In this case, you'll hardcode it as a static value in the query (Step 4). This means you need one query per conversion action. --- ### Step 4: Write the SQL Query Build a query that pulls the records to retract. The query runs against your data warehouse's offline conversion import table. **Query structure:** ```sql SELECT gclid AS "Google Click ID", '' AS "Conversion Name", -- Static value, e.g., 'MQL' conversion_time AS "Conversion Time", -- Original conversion timestamp 'RETRACT' AS "Adjustment Type" FROM offline_conversion_import -- Or whatever your table is named WHERE conversion_mql > 0 -- Only rows where this conversion fired AND closed_loss_reason IN ( 'AutoDQ', 'Not in private practice', 'No longer in private practice', 'DIY DQ', 'Spam', 'Unsubscribed' -- Use the exact list from Step 1 ) ``` **Critical details:** 1. **`` must exactly match Google Ads.** If your Google Ads conversion is called "MQL - Form Submit", the static value must be `'MQL - Form Submit'` — not `'MQL'`, not `'mql - form submit'`. Case-sensitive. Whitespace-sensitive. 2. **The `WHERE` clause on conversion count** (e.g., `conversion_mql > 0`) ensures you only retract records where that specific conversion actually fired. Without this filter, you'd try to retract conversions that never existed, which Google will reject. 3. **The closed-loss reasons must use the exact CRM values.** If HubSpot stores it as `"Auto DQ"` (with a space) and your query says `'AutoDQ'` (no space), you'll get zero results. Pull the actual values from the CRM first. 4. **Date filtering is optional but recommended.** Google Ads can only process retractions within the last 60 days. Adding `AND conversion_time >= DATEADD(day, -60, GETDATE())` reduces the payload and avoids upload errors on old records. **Validation before proceeding:** - Run the query and check the row count. Sanity-check it: - If you get 0 rows: something is wrong with your filters. Check field names and values. - If you get thousands of rows on a small account: your filters might be too broad. Spot-check a few records. - Compare the count to total leads in that period. If you're retracting more than ~40-50% of all conversions, double-check with the client that this is expected. --- ### Step 5: Create the Google Sheet **Do not start from scratch.** Copy an existing retraction sheet from another account to use as a template. This ensures the column headers and formatting are exactly what Google Ads expects. 1. Find an existing retraction sheet from another client's offline uploads folder 2. Make a copy: `File → Make a copy` 3. Save the copy in the client's offline uploads folder (Google Drive), named clearly: `[Client Name] - MQL Retraction Upload` 4. Update the sheet's data connection to point to the new SQL query from Step 4: - Open the sheet's data connector (usually under `Data → Data connectors` or similar, depending on your warehouse integration) - Replace the old query with your new query - Test the connection — click "Refresh" or "Preview" and confirm data appears 5. Verify the columns match what Google Ads expects: - Column A: `Google Click ID` - Column B: `Conversion Name` - Column C: `Conversion Time` - Column D: `Adjustment Type` - **No extra columns.** No header formatting. No merged cells. Google Ads parses this file rigidly. **Common mistakes:** - Extra whitespace in column headers (e.g., `"Google Click ID "` with a trailing space) — will cause upload failure - Date format mismatch — Google wants `yyyy-MM-dd HH:mm:ss` or similar. If your warehouse outputs `MM/dd/yyyy`, you'll need to reformat in the Sheet. - Sheet has multiple tabs — Google Ads reads the first tab only. Make sure data is on Sheet1. --- ### Step 6: Schedule the Sheet Refresh The Google Sheet needs to pull fresh data from your warehouse automatically so new DQ'd leads are included without manual intervention. 1. In the Google Sheet, go to the data connector settings 2. Set **automatic refresh** to every **24 hours** 3. Choose a refresh time that's at least 2-3 hours *before* the Google Ads upload schedule (Step 7). This ensures the sheet has fresh data when Google pulls it. - Example: If the Google Ads upload runs at 8 AM, schedule the sheet refresh for 5 AM. 4. Verify the first automatic refresh runs successfully — check the sheet the next day. **If your data warehouse doesn't support scheduled refresh natively:** - Some connectors require a paid tier for scheduled refresh (e.g., Supermetrics) - Alternatively, set up a Google Apps Script trigger that calls `SpreadsheetApp.flush()` or your connector's refresh API on a timer - As a last resort, set a calendar reminder to manually refresh weekly (not ideal, but better than never) --- ### Step 7: Set Up the Upload Schedule in Google Ads 1. In Google Ads, navigate to: **Tools & Settings → Measurement → Conversions** 2. Click **Uploads** in the left sidebar 3. Click **Schedules** tab 4. Click the **+** button to create a new schedule 5. Select **Google Sheets** as the source 6. Authenticate and select the Google Sheet you created in Step 5 7. Set frequency to **Daily** 8. Set the time to run *after* the Sheet refresh completes (see Step 6 timing note) 9. Save the schedule **After saving, manually trigger the first upload** to verify everything works: - Go to Uploads → click "Upload" → select the same Sheet → Upload - Check the results: - **"Successfully processed"** = working correctly - **"Partially processed"** = some rows failed. Download the error report and check why. Common issues: - GCLID not found (click happened too long ago, or GCLID is from a different Google Ads account) - Conversion name doesn't match any conversion action - Conversion time is outside the 60-day window - Duplicate retraction (already retracted this conversion) - **"Failed"** = structural issue with the file (wrong columns, wrong format, etc.) **The 60-day window:** Google Ads can only process retractions for conversions that happened within the last 60 days. Older records will be rejected. This is expected and not an error — it just means you can't retroactively clean up historical data beyond ~2 months. The ongoing daily schedule handles future DQ'd leads automatically. --- ### Step 8: Repeat for Each Conversion Action Each conversion action requires its own complete pipeline: | Conversion Action | Query | Sheet | Upload Schedule | |-------------------|-------|-------|-----------------| | MQL | Filters for `conversion_mql > 0` | `[Client] - MQL Retraction` | Daily, 8 AM | | SQL | Filters for `conversion_sql > 0` | `[Client] - SQL Retraction` | Daily, 8 AM | For each additional conversion action: 1. Duplicate the Google Sheet from Step 5 (don't create from scratch) 2. Update the `` static value in the query to match the new conversion action 3. Update the `WHERE` clause to filter for that specific conversion column (e.g., `conversion_sql > 0` instead of `conversion_mql > 0`) 4. Create a new upload schedule in Google Ads pointing to the new sheet 5. Test with a manual upload **Naming convention for sheets:** `[Client Name] - [Conversion Action] Retraction Upload` - e.g., `Acme Corp - MQL Retraction Upload` - e.g., `Acme Corp - SQL Retraction Upload` --- ## Post-Setup Verification Checklist After everything is live, verify the system end-to-end: - [ ] **Sheet refresh works** — Check the Sheet 24 hours later. Row count should update if new DQ'd leads exist. - [ ] **Upload runs on schedule** — Check Google Ads → Uploads → History. You should see a daily entry. - [ ] **Retractions are processing** — In the upload history, click into a run and confirm rows are being processed (not all failing). - [ ] **Conversion counts are adjusting** — In Google Ads reporting, retracted conversions will reduce the reported conversion count for the day they originally occurred. This may take 24-48 hours to reflect. - [ ] **No false retractions** — Spot-check 5-10 retracted records in the CRM. Confirm they are genuinely DQ'd leads that should not count as conversions. --- ## Ongoing Maintenance **Monthly check-in:** - Review upload history for persistent errors - Check if new closed-loss reasons have been added to the CRM that should be included in the retraction list - Verify the data source connection hasn't expired (some warehouse connectors require periodic re-auth) **When adding a new conversion action later:** - Copy an existing retraction sheet - Update the conversion name and query filter - Create a new upload schedule - Do NOT modify the existing sheets — each conversion action stays isolated **When the client adds new DQ reasons:** - Update the `IN (...)` list in the SQL query for every retraction sheet - Document the change and the date - The next sheet refresh will automatically include the new reason --- ## Troubleshooting | Problem | Likely Cause | Fix | |---------|-------------|-----| | Upload shows 0 rows processed | Sheet is empty or has wrong headers | Check Sheet data and column names | | "Conversion action not found" errors | Conversion name in Sheet doesn't match Google Ads exactly | Copy-paste the name from Google Ads | | "GCLID not recognized" errors | Click is from a different account or older than 60 days | Filter query to last 60 days; verify account ID | | Upload succeeds but conversion counts unchanged | Retractions take 24-48hrs to reflect in reporting | Wait and re-check | | Sheet stops refreshing | Data warehouse auth expired | Re-authenticate the connector | | Row count keeps growing indefinitely | Query isn't date-filtered, accumulating all historical DQs | Add date filter to limit to last 60 days | --- ## Key Principles 1. **Retract quality, not intent.** Bad leads = retract. Good leads who didn't buy = keep. 2. **Exact naming matters.** Conversion names, column headers, and CRM field values must match precisely. 3. **One sheet per conversion action.** Don't combine MQL and SQL retractions in one file. 4. **The 60-day limit is real.** Don't stress about historical data. Focus on catching DQ'd leads going forward. 5. **Test before scheduling.** Always run a manual upload first to catch formatting issues before the daily schedule goes live. --- # Access Google SEO APIs for search data and performance metrics Query Google Search Console, PageSpeed Insights, CrUX, Indexing API, and GA4 organic data. Real field metrics, indexation status, and search performance. **Canonical:** https://www.marketingskills.sh/AgriciDaniel/claude-seo/seo-google **Source:** https://github.com/AgriciDaniel/claude-seo/tree/main/skills/seo-google **Category:** SEO **Score:** 4.8 **Tags:** google-apis, search-console, core-web-vitals, crux, ga4, indexation --- name: seo-google description: > Google SEO APIs: Search Console (Search Analytics, URL Inspection, Sitemaps), PageSpeed Insights v5, CrUX field data with 25-week history, Indexing API v3, and GA4 organic traffic. Provides real Google field data for Core Web Vitals, indexation status, search performance, and organic traffic trends. Use when user says "search console", "GSC", "PageSpeed", "CrUX", "field data", "indexing API", "GA4 organic", "URL inspection", "google api setup", "real CWV data", "impressions", "clicks", "CTR", "position data", "LCP", "INP", "CLS", "FCP", "TTFB", or "Lighthouse scores". user-invokable: true argument-hint: "[command] [url|property]" license: MIT metadata: author: AgriciDaniel version: "1.8.2" category: seo --- # Google SEO APIs Direct access to Google's own SEO data. Bridges the gap between crawl-based analysis (existing claude-seo skills) and Google's real-time field data: actual Chrome user metrics, real indexation status, search performance, and organic traffic. All APIs are free. Setup requires a Google Cloud project with API key and/or service account -- run `/seo google setup` for step-by-step instructions. ## Prerequisites Before executing any command, check credentials: ```bash python scripts/google_auth.py --check --json ``` Config file: `~/.config/claude-seo/google-api.json` ```json { "service_account_path": "/path/to/service_account.json", "api_key": "AIzaSy...", "default_property": "sc-domain:example.com", "ga4_property_id": "properties/123456789" } ``` If missing, read `references/auth-setup.md` and walk the user through setup. ### Credential Tiers | Tier | Detection | Available Commands | |------|-----------|-------------------| | **0** (API Key) | `api_key` present | `pagespeed`, `crux`, `crux-history`, `youtube`, `nlp` | | **1** (OAuth/SA) | + OAuth token or service account | Tier 0 + `gsc`, `inspect`, `sitemaps`, `index` | | **2** (Full) | + `ga4_property_id` configured | Tier 1 + `ga4`, `ga4-pages` | | **3** (Ads) | + `ads_developer_token` + `ads_customer_id` | Tier 2 + `keywords`, `volume` | Always communicate the detected tier before running commands. ## Quick Reference | Command | What it does | Tier | |---------|-------------|------| | `/seo google setup` | Check/configure API credentials | -- | | `/seo google pagespeed ` | PSI Lighthouse + CrUX field data | 0 | | `/seo google crux ` | CrUX field data only (p75 metrics) | 0 | | `/seo google crux-history ` | 25-week CWV trend analysis | 0 | | `/seo google gsc ` | Search Console: clicks, impressions, CTR, position | 1 | | `/seo google inspect ` | URL Inspection: index status, canonical, crawl info | 1 | | `/seo google inspect-batch ` | Batch URL Inspection from file | 1 | | `/seo google sitemaps ` | GSC sitemap status | 1 | | `/seo google index ` | Submit URL to Indexing API | 1 | | `/seo google index-batch ` | Batch submit up to 200 URLs | 1 | | `/seo google ga4 [property-id]` | GA4 organic traffic report | 2 | | `/seo google ga4-pages [property-id]` | Top organic landing pages | 2 | | `/seo google youtube ` | YouTube video search (views, likes, duration) | 0 | | `/seo google youtube-video ` | YouTube video details + top comments | 0 | | `/seo google nlp ` | NLP entity extraction + sentiment + classification | 0 | | `/seo google entities ` | Entity analysis only (for E-E-A-T) | 0 | | `/seo google keywords ` | Keyword ideas from Google Ads Keyword Planner | 3 | | `/seo google volume ` | Search volume lookup from Keyword Planner | 3 | | `/seo google entity ` | Knowledge Graph entity check | 0 | | `/seo google safety ` | Web Risk URL safety check | 0 | | `/seo google quotas` | Show rate limits for all APIs | -- | --- ## PageSpeed + CrUX ### `/seo google pagespeed ` Combined Lighthouse lab data + CrUX field data. **Script:** `python scripts/pagespeed_check.py --json` **Reference:** `references/pagespeed-crux-api.md` **Default:** Both mobile + desktop strategies, all Lighthouse categories. Output merges lab scores (point-in-time Lighthouse) with field data (28-day Chrome user metrics). CrUX tries URL-level first, falls back to origin-level. ### `/seo google crux ` CrUX field data only (no Lighthouse run). Faster. **Script:** `python scripts/pagespeed_check.py --crux-only --json` ### `/seo google crux-history ` 25-week CrUX History trends. Shows whether CWV metrics are improving, stable, or degrading. **Script:** `python scripts/crux_history.py --json` **Reference:** `references/pagespeed-crux-api.md` Output includes per-metric trend direction, percentage change, and weekly p75 values. --- ## Search Console ### `/seo google gsc ` Search Analytics: clicks, impressions, CTR, position for last 28 days. **Script:** `python scripts/gsc_query.py --property --json` **Reference:** `references/search-console-api.md` **Default:** 28 days, dimensions=query,page, type=web, limit=1000. Includes quick-win detection: queries at position 4-10 with high impressions. ### `/seo google inspect ` URL Inspection: real indexation status from Google. **Script:** `python scripts/gsc_inspect.py --json` Returns: verdict (PASS/FAIL), coverage state, robots.txt status, indexing state, page fetch state, canonical selection, mobile usability, rich results. ### `/seo google inspect-batch ` Batch inspection from a file (one URL per line). Rate limited to 2,000/day per site. **Script:** `python scripts/gsc_inspect.py --batch --json` ### `/seo google sitemaps ` List submitted sitemaps with status, errors, warnings. **Script:** `python scripts/gsc_query.py sitemaps --property --json` --- ## Indexing API ### `/seo google index ` Notify Google of a URL update. **Script:** `python scripts/indexing_notify.py --json` **Reference:** `references/indexing-api.md` The Indexing API is officially for JobPosting and BroadcastEvent/VideoObject pages. Always inform the user of this restriction. Daily quota: 200 publish requests. ### `/seo google index-batch ` Batch submit URLs from a file. Tracks quota usage. **Script:** `python scripts/indexing_notify.py --batch --json` --- ## GA4 Traffic ### `/seo google ga4 [property-id]` Organic traffic report: daily sessions, users, pageviews, bounce rate, engagement. **Script:** `python scripts/ga4_report.py --property --json` **Reference:** `references/ga4-data-api.md` **Default:** 28 days, filtered to Organic Search channel group. ### `/seo google ga4-pages [property-id]` Top organic landing pages ranked by sessions. **Script:** `python scripts/ga4_report.py --property --report top-pages --json` --- ## YouTube (Video SEO) YouTube mentions have the strongest AI visibility correlation (0.737). Free, API key only. ### `/seo google youtube ` Search YouTube for videos. Returns title, channel, views, likes, duration. **Script:** `python scripts/youtube_search.py search "" --json` **Reference:** `references/youtube-api.md` **Quota:** 100 units per search (10,000 units/day free). ### `/seo google youtube-video ` Detailed video info + tags + top 10 comments. **Script:** `python scripts/youtube_search.py video --json` **Quota:** 2 units (video details + comments). --- ## NLP Content Analysis Google's own entity/sentiment analysis. Enhances E-E-A-T scoring. ### `/seo google nlp ` Full NLP analysis: entities, sentiment, content classification. **Script:** `python scripts/nlp_analyze.py --url --json` or `--text "..."` **Reference:** `references/nlp-api.md` **Free tier:** 5,000 units/month. Requires billing enabled on GCP project. ### `/seo google entities ` Entity extraction only (faster, less quota). **Script:** `python scripts/nlp_analyze.py --url --features entities --json` --- ## Keyword Research (Google Ads) Gold-standard keyword volume data. Requires Google Ads account. ### `/seo google keywords ` Generate keyword ideas from seed terms. **Script:** `python scripts/keyword_planner.py ideas "" --json` **Reference:** `references/keyword-planner-api.md` **Requires:** Ads developer token + customer ID in config (Tier 3). ### `/seo google volume ` Search volume for specific keywords (comma-separated). **Script:** `python scripts/keyword_planner.py volume "," --json` --- ## Supplementary ### `/seo google entity ` Knowledge Graph entity check. Verifies brand presence. **Reference:** `references/supplementary-apis.md` Uses Knowledge Graph Search API with API key. ### `/seo google safety ` Web Risk API check for malware/social engineering flags. **Reference:** `references/supplementary-apis.md` ### `/seo google quotas` Display rate limits table. Read `references/rate-limits-quotas.md`. --- ## Reports After any analysis command, offer to generate a PDF/HTML report. ### `/seo google report ` Generate a professional PDF report with charts and analytics. **Script:** `python scripts/google_report.py --type --data --domain --format pdf` | Type | Input | Output | |------|-------|--------| | `cwv-audit` | PSI + CrUX + CrUX History data | Core Web Vitals audit with gauges, timelines, distributions | | `gsc-performance` | GSC query data | Search Console report with query tables, quick wins | | `indexation` | Batch inspection data | Indexation status with coverage donut chart | | `full` | All data combined | Comprehensive Google SEO report (all sections) | **Workflow:** 1. Run data collection commands (pagespeed, gsc, inspect-batch, etc.) 2. Save JSON output to file: `python scripts/pagespeed_check.py --json > data.json` 3. Generate report: `python scripts/google_report.py --type cwv-audit --data data.json --domain ` **Convention:** After completing analysis, suggest: "Generate a report? Use `/seo google report `" --- ## Rate Limits | API | Per-Minute | Per-Day | Auth | |-----|-----------|---------|------| | PSI v5 | 240 QPM | 25,000 QPD | API Key | | CrUX + History | 150 QPM (shared) | Unlimited | API Key | | GSC Search Analytics | 1,200 QPM/site | 30M QPD | Service Account | | GSC URL Inspection | 600 QPM | 2,000 QPD/site | Service Account | | Indexing API | 380 RPM | 200 publish/day | Service Account | | GA4 Data API | 10 concurrent | ~25K tokens/day | Service Account | ## Cross-Skill Integration - **seo-audit**: Spawns `seo-google` agent for live CWV + indexation data (conditional) - **seo-technical**: Uses pagespeed_check.py for real CWV field data - **seo-performance**: CrUX field data supplements Lighthouse lab data - **seo-sitemap**: GSC sitemap status shows real crawl/index coverage - **seo-content**: GSC query data informs keyword targeting - **seo-geo**: GSC search appearance data includes AI Overview references ## Output Format - CWV metrics: traffic-light rating (Good / Needs Improvement / Poor) - Performance reports: tables with sortable columns - Always include data freshness note - Save reports as `GOOGLE-API-REPORT-{domain}.md` - Use templates from `assets/templates/` for structured output ## Technical Notes - INP replaced FID on March 12, 2024. Never reference FID. - CLS values from CrUX are string-encoded (e.g., "0.05"). Scripts handle parsing. - CrUX 404 = insufficient traffic, not an auth error. - Search Analytics data has 2-3 day lag. - `round_trip_time` replaced `effectiveConnectionType` in CrUX (Feb 2025). - Custom Search JSON API is closed to new customers (2025). ## Error Handling | Scenario | Action | |----------|--------| | No credentials configured | Run `/seo google setup`. List Tier 0 commands that work with just an API key. | | Service account lacks GSC access | Report error. Instruct: add `client_email` to GSC > Settings > Users > Add. | | CrUX data unavailable (404) | Report insufficient Chrome traffic. Suggest PSI lab data as fallback. | | GA4 property not found | Report error. Show how to find property ID in GA4 Admin > Property Details. | | Indexing API quota exceeded | Report 200/day limit. Suggest prioritizing most important URLs. | | Rate limit (429) | Wait and retry with exponential backoff. Report which API hit the limit. | --- # Secure web applications against OWASP Top 10 vulnerabilities Prevent XSS, SQL injection, CSRF, SSRF, and OWASP Top 10 vulnerabilities with code patterns, threat modeling, and security headers. **Canonical:** https://www.marketingskills.sh/AbsolutelySkilled/AbsolutelySkilled/appsec-owasp **Source:** https://github.com/AbsolutelySkilled/AbsolutelySkilled/tree/main/skills/appsec-owasp **Score:** 4.8 **Tags:** application-security, owasp, web-security, secure-coding, input-validation, authentication --- name: appsec-owasp version: 0.1.0 description: > Use this skill when securing web applications, preventing OWASP Top 10 vulnerabilities, implementing input validation, or designing authentication. Triggers on XSS, SQL injection, CSRF, SSRF, broken authentication, security headers, input validation, output encoding, OWASP, and any task requiring application security hardening. category: engineering tags: [security, owasp, xss, sql-injection, authentication, appsec] recommended_skills: [penetration-testing, cloud-security, cryptography, security-incident-response] platforms: - claude-code - gemini-cli - openai-codex license: MIT maintainers: - github: maddhruv --- When this skill is activated, always start your first response with the 🧢 emoji. # AppSec - OWASP Top 10 A practitioner's guide to application security based on the OWASP Top 10 2021. This skill covers the full lifecycle of web application security - from threat modeling to concrete code patterns for preventing injection, authentication failures, XSS, CSRF, SSRF, and misconfiguration. Designed for developers who need security guidance at the code level, not just as policy. --- ## When to use this skill Trigger this skill when the user: - Asks how to prevent XSS, SQL injection, CSRF, or SSRF - Implements or reviews authentication / session management - Sets security headers (CSP, HSTS, X-Frame-Options, etc.) - Validates or sanitizes user input - Designs authorization logic or access controls - Reviews code for OWASP Top 10 vulnerabilities - Asks about output encoding, parameterized queries, or allowlists Do NOT trigger this skill for: - Network-level security (firewalls, VPNs, DDoS mitigation) - use a network security skill instead - Secrets management / key rotation workflows - use a secrets management skill for those operational concerns --- ## Key principles 1. **Never trust user input** - All data from the outside world is untrusted: HTTP bodies, headers, query params, cookies, uploaded files, and even data read back from your own database that originated from user input. 2. **Defense in depth** - Apply multiple independent security controls. If one layer fails, the next one stops the attack. Never rely on a single control. 3. **Least privilege** - Every component (user accounts, DB connections, API tokens, OS processes) should have only the permissions required and nothing more. Blast radius is limited by privilege scope. 4. **Fail securely** - When something goes wrong, default to the most restrictive outcome. Deny access on error, not grant it. Surface a generic error message to users, log the detail server-side. 5. **Security by default** - Secure configuration should be the default state. Developers should have to explicitly opt out of security controls, not opt in. --- ## Core concepts ### OWASP Top 10 2021 | Rank | Category | Root cause | Typical impact | |------|----------|------------|----------------| | A01 | Broken Access Control | Missing server-side checks, IDOR | Data breach, privilege escalation | | A02 | Cryptographic Failures | Weak algorithms, missing TLS, plain-text PII | Data exposure, credential theft | | A03 | Injection (SQL, NoSQL, OS, LDAP) | String-concatenated queries | Data breach, RCE, data destruction | | A04 | Insecure Design | No threat model, missing abuse cases | Business logic bypass | | A05 | Security Misconfiguration | Defaults unchanged, debug on in prod | Information disclosure, RCE | | A06 | Vulnerable and Outdated Components | Unpinned deps, no CVE scanning | Range from XSS to full compromise | | A07 | Identification and Auth Failures | Weak passwords, no MFA, bad session mgmt | Account takeover | | A08 | Software and Data Integrity Failures | Unsigned artifacts, insecure deserialization | Supply chain attack, RCE | | A09 | Security Logging and Monitoring Failures | No audit trail, no alerting | Undetected breach, slow response | | A10 | SSRF | User-controlled URLs fetched server-side | Internal network access, cloud metadata theft | ### Threat modeling basics Before writing security controls, answer four questions: 1. **What are we building?** - Draw a data-flow diagram including trust boundaries 2. **What can go wrong?** - Use STRIDE (Spoofing, Tampering, Repudiation, Info Disclosure, Denial of Service, Elevation of Privilege) 3. **What are we going to do about it?** - For each threat, decide: mitigate, accept, transfer, or eliminate 4. **Did we do a good enough job?** - Validate controls cover identified threats Run threat modeling at design time, not after the code is written. ### Security headers quick reference | Header | Recommended value | Defends against | |--------|-------------------|-----------------| | `Content-Security-Policy` | `default-src 'self'; script-src 'self'` | XSS via inline scripts and external resources | | `Strict-Transport-Security` | `max-age=63072000; includeSubDomains; preload` | Protocol downgrade, cookie hijacking | | `X-Content-Type-Options` | `nosniff` | MIME-type confusion attacks | | `X-Frame-Options` | `DENY` | Clickjacking | | `Referrer-Policy` | `strict-origin-when-cross-origin` | Referrer leakage | | `Permissions-Policy` | `camera=(), microphone=(), geolocation=()` | Browser feature misuse | See `references/security-headers.md` for full CSP directive reference and frame-ancestors vs X-Frame-Options comparison. --- ## Common tasks ### Prevent XSS with output encoding Never insert untrusted data into HTML without context-aware encoding. The encoding rule depends on where in the HTML the data lands. ```typescript import DOMPurify from 'dompurify'; import { escape } from 'html-escaper'; // 1. HTML context - escape <, >, &, ", ' function renderComment(userInput: string): string { return escape(userInput); // safe: <script> not executed } // 2. When you must allow some HTML (e.g. rich text) - sanitize, don't escape function renderRichText(userHtml: string): string { // DOMPurify strips disallowed tags/attributes; allowlist only what you need return DOMPurify.sanitize(userHtml, { ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'ul', 'li'], ALLOWED_ATTR: ['href', 'title'], }); } // 3. JavaScript context - use JSON.stringify, never template-inject // WRONG: // RIGHT: function inlineJsonData(data: unknown): string { // JSON.stringify encodes <, >, & to unicode escapes automatically return ``; } ``` > Set `Content-Security-Policy: default-src 'self'; script-src 'self'` so that > even if encoding fails, inline scripts are blocked by the browser. ### Prevent SQL injection with parameterized queries Never concatenate user input into SQL strings. Always use parameterized queries or a safe ORM layer. ```typescript import { Pool } from 'pg'; const pool = new Pool(); // WRONG - string interpolation: // const rows = await pool.query(`SELECT * FROM users WHERE email = '${email}'`); // RIGHT - parameterized ($1, $2 for pg): async function findUserByEmail(email: string) { const { rows } = await pool.query( 'SELECT id, name, email FROM users WHERE email = $1', [email] ); return rows[0] ?? null; } // RIGHT - ORM (Prisma example): // const user = await prisma.user.findUnique({ where: { email } }); // Dynamic ORDER BY (column names can't be parameterized - use an allowlist): const ALLOWED_SORT_COLUMNS = new Set(['name', 'created_at', 'email'] as const); async function listUsers(sortBy: string, order: 'ASC' | 'DESC') { if (!ALLOWED_SORT_COLUMNS.has(sortBy as any)) { throw new Error(`Invalid sort column: ${sortBy}`); } const direction = order === 'DESC' ? 'DESC' : 'ASC'; // only two valid values const { rows } = await pool.query( `SELECT id, name FROM users ORDER BY ${sortBy} ${direction}` ); return rows; } ``` ### Implement CSRF protection For detailed CSRF token pattern and SameSite cookie implementations, see `references/auth-csrf-patterns.md`. ### Set security headers (CSP, HSTS, X-Frame-Options) ```typescript import helmet from 'helmet'; import { Express } from 'express'; function applySecurityHeaders(app: Express): void { app.use( helmet({ // HSTS: force HTTPS for 2 years, include subdomains, add to preload list hsts: { maxAge: 63072000, includeSubDomains: true, preload: true, }, // CSP: restrict resource loading to same origin; tighten per-app contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'"], // no inline scripts, no eval styleSrc: ["'self'", "'unsafe-inline'"], // relax only if needed imgSrc: ["'self'", 'data:', 'https://cdn.example.com'], connectSrc: ["'self'", 'https://api.example.com'], fontSrc: ["'self'"], objectSrc: ["'none'"], frameAncestors: ["'none'"], // replaces X-Frame-Options upgradeInsecureRequests: [], }, }, // Clickjacking: frameAncestors in CSP is preferred; keep this as fallback frameguard: { action: 'deny' }, // Prevent MIME sniffing noSniff: true, // Limit referrer leakage referrerPolicy: { policy: 'strict-origin-when-cross-origin' }, // Disable browser features not used by the app permittedCrossDomainPolicies: false, }) ); // Permissions-Policy (not yet in helmet stable - set manually) app.use((_req, res, next) => { res.setHeader( 'Permissions-Policy', 'camera=(), microphone=(), geolocation=(), payment=()' ); next(); }); } ``` ### Implement secure authentication (bcrypt, JWT, session) For detailed bcrypt password hashing, JWT issuance, and secure login handler implementations, see `references/auth-csrf-patterns.md`. ### Prevent SSRF Validate and restrict any URL your server fetches on behalf of a user request. ```typescript import { URL } from 'url'; import dns from 'dns/promises'; import { isPrivate } from 'private-ip'; // npm i private-ip const ALLOWED_SCHEMES = new Set(['https:']); const ALLOWED_HOSTS = new Set(['api.example.com', 'cdn.example.com']); async function isSafeUrl(rawUrl: string): Promise { let parsed: URL; try { parsed = new URL(rawUrl); } catch { return false; // not a valid URL } // 1. Allowlist scheme if (!ALLOWED_SCHEMES.has(parsed.protocol)) return false; // 2. If you can't use a host allowlist, at least block private/internal ranges if (!ALLOWED_HOSTS.has(parsed.hostname)) { // Resolve the hostname and check its IP try { const addresses = await dns.lookup(parsed.hostname, { all: true }); for (const { address } of addresses) { if (isPrivate(address)) return false; // blocks 10.x, 172.16-31.x, 192.168.x, 127.x, etc. } } catch { return false; // DNS resolution failure - deny } } return true; } async function fetchWebhook(userProvidedUrl: string, payload: unknown) { if (!(await isSafeUrl(userProvidedUrl))) { throw new Error('URL not allowed'); } // Proceed with fetch - also set a tight timeout const res = await fetch(userProvidedUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), signal: AbortSignal.timeout(5000), // 5-second hard timeout }); return res; } ``` ### Input validation with allowlists Reject anything that doesn't match your expected format. Allowlists are far safer than blocklists because attackers find encodings you didn't block. ```typescript import { z } from 'zod'; // npm i zod // Define strict schemas - unknown fields are stripped by default const CreateUserSchema = z.object({ email: z.string().email().max(254).toLowerCase(), name: z.string().min(1).max(100).regex(/^[\p{L}\p{N} '-]+$/u), // letters, digits, space, hyphen, apostrophe role: z.enum(['viewer', 'editor', 'admin']), // strict allowlist, not a free string age: z.number().int().min(13).max(120).optional(), }); type CreateUserInput = z.infer; function validateCreateUser(body: unknown): CreateUserInput { // parse() throws ZodError with field-level detail on failure return CreateUserSchema.parse(body); } // Use in Express middleware import { Request, Response, NextFunction } from 'express'; function validateBody(schema: z.ZodSchema) { return (req: Request, res: Response, next: NextFunction) => { const result = schema.safeParse(req.body); if (!result.success) { res.status(400).json({ error: 'Validation failed', issues: result.error.flatten().fieldErrors, }); return; } req.body = result.data; // replace with validated + stripped data next(); }; } // router.post('/users', validateBody(CreateUserSchema), createUserHandler); ``` --- ## Anti-patterns | Anti-pattern | Why it's dangerous | What to do instead | |---|---|---| | String-concatenating SQL | Allows injection; attacker can terminate the query and append arbitrary SQL | Always use parameterized queries or ORM bind parameters | | Storing passwords as MD5/SHA-256 | Fast hashes are brute-forceable; rainbow tables precomputed | Use bcrypt (cost 12+) or Argon2id | | Putting JWT in localStorage | XSS can read localStorage and steal the token | Store JWT in httpOnly, Secure, SameSite cookie | | Reflecting the Origin header in CORS | Equivalent to `Access-Control-Allow-Origin: *` with no audit trail | Maintain an explicit allowlist of allowed origins | | Using blocklists for input validation | Encodings, Unicode variants, and novel payloads bypass blocklists | Use allowlists - define exactly what is valid and reject everything else | | Fetching user-supplied URLs without validation | SSRF: attacker reaches internal services, cloud metadata endpoint (169.254.169.254) | Validate scheme, resolve DNS, reject private IP ranges; prefer a host allowlist | --- ## Gotchas 1. **DNS rebinding bypasses IP-based SSRF blocklists** - An attacker registers a domain that initially resolves to a public IP (passing your IP check), then immediately re-resolves to `169.254.169.254` (cloud metadata). The server fetches the attacker's internal target. Mitigate by using a host allowlist, not just an IP blocklist, or by caching the resolved IP and using it for the actual connection. 2. **`bcrypt.compare()` must always run even for missing users** - If you return early with "user not found" before calling `bcrypt.compare()`, the response time is measurably shorter than a failed password check. Timing-based enumeration reveals valid email addresses. Always run `bcrypt.compare()` against a dummy hash even when the user doesn't exist. 3. **CSP `unsafe-inline` on `script-src` negates XSS protection** - Adding `'unsafe-inline'` to `script-src` allows all inline scripts, which is what CSP exists to prevent. If you need inline styles, use `'unsafe-inline'` on `style-src` only. For inline scripts, use nonces or hashes instead. 4. **`SameSite=Lax` doesn't protect non-GET state-changing requests on cross-site navigation** - Top-level navigations with GET are allowed under `SameSite=Lax`. For mutation endpoints invoked via form POST from another origin, `Lax` provides no protection. Use `SameSite=Strict` or implement CSRF tokens for server-rendered form submissions. 5. **Dynamic `ORDER BY` column names can't be parameterized and are injection vectors** - You can't use `$1` for a column name or SQL keyword. A `sortBy` query parameter passed directly into `ORDER BY ${sortBy}` is injectable. Always validate against an explicit allowlist of permitted column names before interpolating. --- ## References For deeper implementation guidance, load the relevant reference file: - `references/security-headers.md` - Full CSP directive reference, HSTS preloading, frame-ancestors vs X-Frame-Options, Permissions-Policy - `references/auth-csrf-patterns.md` - CSRF token pattern, SameSite cookie setup, bcrypt/JWT authentication implementation --- ## Companion check > On first activation of this skill in a conversation: check which companion skills are installed by running `ls ~/.claude/skills/ ~/.agent/skills/ ~/.agents/skills/ .claude/skills/ .agent/skills/ .agents/skills/ 2>/dev/null`. Compare the results against the `recommended_skills` field in this file's frontmatter. For any that are missing, mention them once and offer to install: > ``` > npx skills add AbsolutelySkilled/AbsolutelySkilled --skill > ``` > Skip entirely if `recommended_skills` is empty or all companions are already installed. --- # Check affiliate content for FTC compliance and platform rules Audit affiliate content for FTC disclosure, prohibited claims, and platform rules. Detects issues and provides copy-paste-ready fixes. **Canonical:** https://www.marketingskills.sh/Affitor/affiliate-skills/compliance-checker **Source:** https://github.com/Affitor/affiliate-skills/tree/main/skills/meta/compliance-checker **Category:** Strategy **Score:** 4.8 **Tags:** compliance, affiliate-marketing, ftc, disclosure, legal, content-audit --- name: compliance-checker description: > Check affiliate content for FTC compliance and platform rules. Triggers on: "check my content for compliance", "FTC disclosure check", "is this legal", "review for compliance", "check affiliate disclosure", "am I FTC compliant", "audit my content", "compliance review", "legal check", "platform rules check", "check before publishing", "disclosure audit", "review my ad copy". license: MIT version: "1.0.0" tags: ["affiliate-marketing", "meta", "planning", "compliance", "ftc", "disclosure"] compatibility: "Claude Code, ChatGPT, Gemini CLI, Cursor, Windsurf, OpenClaw, any AI agent" metadata: author: affitor version: "1.0" stage: S8-Meta --- # Compliance Checker Audit affiliate content for FTC compliance, platform-specific rules, and legal requirements. Checks disclosure placement, prohibited claims, endorsement guidelines, and platform policies. Output is a compliance scorecard with issues, severity, and fix suggestions. ## Stage S8: Meta — The FTC has fined affiliates $4.2M+ for undisclosed endorsements. One missing disclosure can result in legal action, platform bans, or program termination. This skill is the safety net — run it on any content before publishing to catch compliance issues before they become problems. ## When to Use - User wants to check content before publishing - User asks about FTC rules or affiliate disclosure requirements - User is unsure if their content is compliant - User says "is this legal?", "do I need a disclosure?", "check my post" - User is preparing content for a platform with strict ad policies (Facebook, Google) - Chaining: run after any S2-S5 or S7 content-producing skill before publishing - User wants to audit existing published content ## Input Schema ```yaml content: string # REQUIRED — the content to check (text, markdown, or HTML) content_type: string # REQUIRED — "social_post" | "blog" | "landing_page" # | "email" | "ad" | "video_script" platform: string # OPTIONAL — "linkedin" | "twitter" | "reddit" | "facebook" # | "tiktok" | "youtube" | "google_ads" | "pinterest" # Platform-specific rules applied if provided claims: # OPTIONAL — specific claims to verify - string # e.g., ["earn $10K/month", "guaranteed results"] ``` **Chaining context**: If content was produced by S2-S5 or S7 in the same conversation, pull it directly. The user should not have to paste content that was just generated. ## Workflow ### Step 1: Detect Affiliate Links Scan content for: - URLs with affiliate parameters (`ref=`, `aff=`, `partner=`, UTM tags) - Shortened URLs (bit.ly, etc.) that may hide affiliate links - Product mentions that imply a commercial relationship ### Step 2: Check FTC Disclosure Read `shared/references/ftc-compliance.md` for rules. Check: - **Presence**: Is there a disclosure? (required if any affiliate link exists) - **Placement**: Is the disclosure before or near the affiliate link? (not buried at the bottom) - **Clarity**: Is it clear to a reasonable consumer? ("affiliate link" is clear; "partner" alone is not) - **Format by content type**: - Social post: `#ad` or `Affiliate link` visible without expanding - Blog: Disclosure in the opening paragraph, above the fold - Landing page: Medium disclosure above the fold - Email: Disclosure near the affiliate link - Ad: Platform-specific requirements ### Step 3: Check Prohibited Claims Scan for: - **Income claims**: "earn $X", "make money fast", "passive income guaranteed" - **False urgency**: "only 3 left" (if not verifiable), "offer expires" (if no real deadline) - **Health/medical claims**: unsubstantiated health benefits - **Guaranteed results**: "guaranteed to work", "100% success rate" - **Fake scarcity**: "limited spots" (if not actually limited) - **Fake testimonials**: results that aren't typical without disclaimer ### Step 4: Check Platform Rules If `platform` is provided, apply platform-specific rules: - **Reddit**: Self-promotion rules (10:1 ratio), must disclose in post - **Facebook/Instagram**: Branded Content tool, "Paid Partnership" label for ads - **Google Ads**: Clear commercial intent, no misleading claims, landing page requirements - **TikTok**: #ad or Paid Partnership toggle, no medical/financial advice claims - **YouTube**: Verbal + written disclosure in first 30 seconds, "Includes paid promotion" checkbox ### Step 5: Score and Report Rate compliance on three levels: - **PASS**: No issues found - **WARN**: Minor issues that should be fixed (e.g., disclosure placement could be better) - **FAIL**: Critical issues that must be fixed before publishing (e.g., no disclosure at all) ### Step 6: Generate Fixes For each issue, provide: - What's wrong (specific quote from content) - Why it matters (rule reference) - How to fix it (specific replacement text) ### Step 7: Self-Validation Before presenting output, verify: - [ ] All affiliate links in the content are detected and flagged - [ ] Disclosure placement check matches platform-specific rules - [ ] Prohibited claims identified with exact quotes from content - [ ] Fix suggestions are copy-paste ready and preserve original tone - [ ] Corrected content would pass a re-scan by this same skill If any check fails, fix the output before delivering. Do not flag the checklist to the user — just ensure the output passes. ## Output Schema ```yaml output_schema_version: "1.0.0" # Semver — bump major on breaking changes compliance: overall_score: string # "PASS" | "WARN" | "FAIL" disclosure_present: boolean disclosure_placement: string # "correct" | "needs_improvement" | "missing" prohibited_claims: number # count of issues found platform_issues: number # count of platform-specific issues issues: - severity: string # "critical" | "warning" | "info" category: string # "disclosure" | "claims" | "platform" | "formatting" description: string # what's wrong quote: string # the problematic text fix: string # suggested replacement corrected_content: string # full content with all fixes applied ``` ## Output Format 1. **Compliance Scorecard** — overall score, disclosure status, issue counts 2. **Issues Found** — table with severity, category, description, and fix 3. **Corrected Content** — the full content with all issues fixed (copy-paste ready) 4. **Platform Notes** — any platform-specific requirements not yet addressed ## Error Handling - **No content provided**: "Paste the content you want me to check, or tell me which skill output to review. I'll check it for FTC compliance and platform rules." - **Content has no affiliate links**: "No affiliate links detected. FTC disclosure is only required for content with material connections (affiliate links, sponsored content, gifted products). Your content looks clean." - **Unknown platform**: "I don't have specific rules for [platform]. I'll check general FTC compliance. For platform-specific rules, check the platform's advertising policy page." ## Examples ### Example 1: Social post with missing disclosure **User**: "Check this tweet: 'Just tried HeyGen and it's incredible for creating AI videos. Use my link to get 10% off: heygen.com/ref/abc123'" **Action**: FAIL — no FTC disclosure. Fix: Add `#ad` before or after the link. Output corrected tweet with disclosure. ### Example 2: Blog post with buried disclosure **User**: [Pastes a 1000-word blog review with disclosure only in the footer] **Action**: WARN — disclosure present but buried at bottom. Fix: Move disclosure to opening paragraph. Also check for income claims, link attributes (`rel="nofollow sponsored"`). ### Example 3: Facebook ad with income claim **User**: "Check this ad: 'I made $5,000 last month with this one tool. You can too! Click here to start earning.'" **Action**: FAIL — (1) income claim without typicality disclaimer, (2) no FTC disclosure, (3) Facebook requires Paid Partnership label. Output fixes for all three issues. ## References - `shared/references/ftc-compliance.md` — FTC affiliate disclosure requirements. Read in Step 2. - `shared/references/affitor-branding.md` — Branding guidelines. Referenced for page outputs. - `shared/references/flywheel-connections.md` — master flywheel connection map ## Revenue & Action Plan ### Expected Outcomes - **Revenue protection**: The FTC has fined affiliates $4.2M+ for undisclosed endorsements. A single violation can result in program termination (losing ALL commissions), platform bans, or legal action. Compliance isn't about making money — it's about not losing the money you've already earned - **Benchmark**: Amazon Associates terminates 10,000+ affiliate accounts per year for policy violations. One missing disclosure on one post can end your entire affiliate business overnight - **Key metric to track**: Compliance score across ALL your published content. Target: 100% PASS. Any FAIL = fix before publishing. Any WARN = fix within 48 hours ### Do This Right Now (15 min) 1. **Fix all FAIL issues immediately** — do not publish until every critical issue is resolved 2. **Fix WARN issues** — update disclosure placement, fix any claim language 3. **Copy the Corrected Content** from the output and replace your original content 4. **Audit your top 5 existing posts** — run this skill on your most-trafficked affiliate content to ensure they're compliant ### Track Your Results Run this skill monthly on all new content before publishing. Keep a compliance log — if the same issue appears 3+ times, update your content creation workflow to prevent it at the source (e.g., add FTC disclosure to your post template). > **Next step — copy-paste this prompt:** > "Plan my entire affiliate funnel from research to revenue" → runs `funnel-planner` ## Flywheel Connections ### Feeds Into - All content skills (S2-S5, S7) — compliance status acts as a pass/fail gate before publishing ### Fed By - All content-producing skills — content to check for compliance - `landing-page-creator` (S4) — landing pages to audit for FTC compliance - `email-drip-sequence` (S5) — emails to check for disclosure ### Feedback Loop - Compliance issues found repeatedly in specific content types → flag to the relevant skill for structural improvement ```yaml chain_metadata: skill_slug: "compliance-checker" stage: "meta" timestamp: string suggested_next: - "self-improver" - "funnel-planner" ``` --- # Diagnose Google Ads tracking reliability and block unsafe optimization Audit conversion tracking setup, assess confidence level, and gate optimization decisions until tracking is trustworthy. **Canonical:** https://www.marketingskills.sh/TheMattBerman/google-ads-copilot/google-ads-tracking **Source:** https://github.com/TheMattBerman/google-ads-copilot/tree/main/skills/google-ads-tracking **Category:** Paid Ads **Score:** 4.8 **Tags:** google-ads, conversion-tracking, diagnosis, trust-and-safety, tracking-compliance, decision-gating --- name: google-ads-tracking description: > Diagnose whether a Google Ads account's conversion tracking is trustworthy enough to optimize. Pulls live data via MCP or works with manual exports. Produces tracking fix drafts when problems are found. --- # Google Ads Tracking Read first: - `google-ads/references/operator-thesis.md` - `google-ads/references/tracking-playbook.md` - `google-ads/references/deliverable-templates.md` Read workspace if available: - `workspace/ads/account.md` - `workspace/ads/goals.md` - `workspace/ads/findings.md` - `workspace/ads/change-log.md` - `workspace/ads/learnings.md` --- ## Data Acquisition ### Connected Mode (MCP available) Pull via the `search` tool on `google-ads-mcp`: **Primary: Conversion actions and configuration:** ```sql SELECT conversion_action.id, conversion_action.name, conversion_action.type, conversion_action.category, conversion_action.status, conversion_action.counting_type, conversion_action.include_in_conversions_metric, conversion_action.value_settings.default_value FROM conversion_action WHERE conversion_action.status = 'ENABLED' ``` **Primary: Conversion performance by action (detect duplicates/pollution):** ```sql SELECT conversion_action.name, conversion_action.type, conversion_action.category, metrics.conversions, metrics.conversions_value, metrics.all_conversions FROM conversion_action WHERE segments.date DURING LAST_30_DAYS AND conversion_action.status = 'ENABLED' ORDER BY metrics.conversions DESC ``` **Supplementary: Account auto-tagging status:** ```sql SELECT customer.id, customer.descriptive_name, customer.auto_tagging_enabled FROM customer ``` **Supplementary: Campaign-level conversion metrics (look for anomalies):** ```sql SELECT campaign.name, metrics.conversions, metrics.all_conversions, metrics.conversions_value, metrics.cost_micros, metrics.cost_per_conversion FROM campaign WHERE campaign.status = 'ENABLED' AND segments.date DURING LAST_30_DAYS ORDER BY metrics.conversions DESC ``` See `data/gaql-recipes.md` for additional queries. ### Date Range Fallback Conversion action configuration queries (no date range) always work. For conversion *performance* queries, if `LAST_30_DAYS` returns zero data, fall back to `LAST_90_DAYS`, then all-time. Configuration queries don't need fallback — they reflect current state regardless. Always state the date range used. ### Export Mode (no MCP) Ask the user for: - Conversion actions list (name, type, counting, include in conversions, value settings) - Google Tag Assistant screenshot or diagnosis notes - GA4 conversion import settings (if applicable) - Any notes on tracking implementation See `data/export-formats.md` for recommended format. --- ## Diagnostic Framework ### 1. Can we trust the account? Assign a tracking confidence level immediately using the rubric in `google-ads/references/tracking-playbook.md`. | Level | Meaning | Action | |-------|---------|--------| | **High** | Clean primary conversion, no duplicates, correct counting, ≥15 conv/30d | Optimize freely | | **Medium** | Minor issues (micro-conversions in primary, 5-14 conv/30d, possible over-count) | Optimize cautiously, fix issues | | **Low** | Major problems (duplicates, wrong counting, <5 conv/30d, polluted signal) | Fix tracking FIRST, block budget scaling | | **Broken** | No meaningful conversion signal | Stop. Fix tracking. Nothing else matters. | **Threshold checklist (from tracking-playbook.md):** - Count primary conversion actions (should be 1-2 clear ones) - Check `include_in_conversions_metric` — are micro-conversions polluting primary? - Compare `conversions` vs `all_conversions` — if ratio is >2x, investigate - Check counting type — "One" for leads, "Every" for e-commerce only - Check for duplicate sources — same event tracked by both native tag AND GA4 import? - Check auto-tagging status - Verify conversion volume (≥15/30d for HIGH, 5-14 for MEDIUM, <5 for LOW) ### 2. What to check **Duplicate counting:** - Same conversion tracked by both native tag AND GA4 import - Multiple conversion actions for the same event - "Every" counting type on lead/form events (should be "One") **Micro-conversion pollution:** - Page views, scroll depth, time on site set as primary conversions - These inflate conversion count and deflate apparent CPA - Smart bidding optimizes toward them instead of real conversions **Missing conversions:** - Key events not tracked at all - Phone calls without call tracking - Form submissions without confirmation page or event tracking **Attribution concerns:** - Auto-tagging disabled (GCLID not passing) - Cross-domain tracking gaps - Conversion window too short or too long **Value quality:** - All conversions have the same default value - Values don't reflect actual revenue - Missing value tracking on e-commerce ### 3. Impact assessment For each problem found, assess: - How much is this distorting reported performance? - Which campaigns are most affected? - Is smart bidding learning from bad signal? - What decisions should wait until this is fixed? ## Draft Output ### Tracking Fix Draft **Trigger:** Any tracking problem at Medium confidence or lower. Create using `drafts/templates/tracking-draft.md`: - Write to `workspace/ads/drafts/YYYY-MM-DD-[account-slug]-tracking-fix.md` - Include specific fixes: what to change, current vs. proposed state - Note the impact on reported conversions ("Conversions will appear to drop ~40%") - Note the impact on smart bidding ("2-3 week recalibration period") - **Critically:** list which optimization decisions should wait until tracking is fixed - Update `workspace/ads/drafts/_index.md` ### Blocking behavior When tracking confidence is Low or Broken, the draft should include a "blocked decisions" section that explicitly names what other skills/drafts should NOT proceed until tracking is resolved: - Budget scaling → blocked - Structure changes → proceed with caution - Negatives → can proceed (waste is waste regardless of tracking) - RSA changes → can proceed (copy quality is independent) ### Always update workspace memory: - `workspace/ads/findings.md` — tracking diagnosis and confidence level - `workspace/ads/account.md` — update tracking status if it has changed - `workspace/ads/learnings.md` — what we learned about this account's tracking ## Output Shape 1. **Account Status block** — account name, CID, status, date range used, tracking confidence, mode 3. Conversion actions inventory (what's tracked, how, counting type) 4. Problems identified (with severity and evidence) 5. Impact assessment (what's distorted, which campaigns affected) 6. Recommended fixes (specific changes) 7. Blocked decisions (what should wait) 8. Draft created (path and summary) 9. Memory updates ## Rules - If tracking is unreliable, say it early. - Do not bury trust issues under optimization tips. - Explain what decisions should wait until tracking is fixed. - **Tracking confidence is a gate. If it's Low or Broken, other skills should know.** - **The most dangerous Google Ads accounts look great on paper because tracking is counting the wrong things. Surface this clearly.** --- # Conduct a Google Ads full-account audit with synthesis and drafted actions Run a comprehensive Google Ads audit that synthesizes tracking, intent, waste, structure, and budget into prioritized action drafts. **Canonical:** https://www.marketingskills.sh/TheMattBerman/google-ads-copilot/google-ads-audit **Source:** https://github.com/TheMattBerman/google-ads-copilot/tree/main/skills/google-ads-audit **Category:** Paid Ads **Score:** 4.8 **Tags:** google-ads, account-audit, prioritization, action-drafts, tracking, intent-mapping --- name: google-ads-audit description: > Full Google Ads review that synthesizes tracking, search terms, intent mapping, negatives, structure, RSAs, budget decisions, and PMax context into a single operator read. Pulls live data via MCP or works with manual exports. Produces a prioritized batch of action drafts. --- # Google Ads Audit This is the broad review. Not just a checklist. A synthesis of what the account is learning, where it is leaking, and what should change. Read first: - `google-ads/references/operator-thesis.md` - `google-ads/references/intent-map.md` - `google-ads/references/query-patterns.md` - `google-ads/references/negatives-playbook.md` - `google-ads/references/tracking-playbook.md` - `google-ads/references/structure-playbook.md` - `google-ads/references/rsa-playbook.md` - `google-ads/references/budget-playbook.md` - `google-ads/references/benchmarks.md` - `google-ads/references/deliverable-templates.md` Read workspace if available: - All `workspace/ads/` files except `assets.md` (unless creative output is needed) - Including `workspace/ads/drafts/_index.md` — check for existing pending drafts --- ## Data Acquisition ### Connected Mode (MCP available) The audit pulls data across all skill areas. Run queries in this order (each feeds into the next): **1. Account overview:** ```sql SELECT customer.id, customer.descriptive_name, customer.currency_code, customer.time_zone, customer.auto_tagging_enabled FROM customer ``` **2. Tracking health (check trust first):** ```sql SELECT conversion_action.name, conversion_action.type, conversion_action.category, conversion_action.counting_type, conversion_action.include_in_conversions_metric, metrics.conversions, metrics.all_conversions FROM conversion_action WHERE segments.date DURING LAST_30_DAYS AND conversion_action.status = 'ENABLED' ORDER BY metrics.conversions DESC ``` **3. Campaign performance and structure:** ```sql SELECT campaign.name, campaign.status, campaign.advertising_channel_type, campaign.bidding_strategy_type, campaign_budget.amount_micros, metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions, metrics.conversions_value, metrics.cost_per_conversion, metrics.search_impression_share, metrics.search_budget_lost_impression_share FROM campaign WHERE campaign.status = 'ENABLED' AND segments.date DURING LAST_30_DAYS ORDER BY metrics.cost_micros DESC ``` **4. Search terms (top 500 by spend):** ```sql SELECT search_term_view.search_term, campaign.name, ad_group.name, metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions, metrics.conversions_value FROM search_term_view WHERE segments.date DURING LAST_30_DAYS ORDER BY metrics.cost_micros DESC LIMIT 500 ``` **Retrieval ladder** — if the search-term query returns no rows, follow the shared retrieval ladder in `data/search-term-retrieval.md`. Report the resulting `retrieval_mode` in the audit header. Mark search-term sections as "PMax visibility-limited" when operating in `pmax-fallback` mode. In `limited` mode, note the gap and request a UI export. **5. Ad group structure:** ```sql SELECT campaign.name, ad_group.name, ad_group.status, metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions FROM ad_group WHERE campaign.status = 'ENABLED' AND ad_group.status = 'ENABLED' AND segments.date DURING LAST_30_DAYS ORDER BY campaign.name ``` **6. Keyword view (targeted keywords — cross-reference with search terms):** ```sql SELECT campaign.name, ad_group.name, ad_group_criterion.keyword.text, ad_group_criterion.keyword.match_type, ad_group_criterion.status, metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions FROM keyword_view WHERE campaign.status = 'ENABLED' AND ad_group.status = 'ENABLED' AND segments.date DURING LAST_30_DAYS ORDER BY metrics.cost_micros DESC LIMIT 200 ``` **7. Existing negatives:** ```sql SELECT campaign.name, campaign_criterion.keyword.text, campaign_criterion.keyword.match_type FROM campaign_criterion WHERE campaign_criterion.negative = TRUE AND campaign_criterion.type = 'KEYWORD' ``` **8. RSA assets (if time permits):** ```sql SELECT asset.text_asset.text, asset.type, ad_group_ad_asset_view.performance_label, ad_group_ad_asset_view.field_type, campaign.name, metrics.impressions, metrics.clicks, metrics.conversions FROM ad_group_ad_asset_view WHERE segments.date DURING LAST_30_DAYS AND campaign.status = 'ENABLED' ORDER BY metrics.impressions DESC ``` See `data/gaql-recipes.md` for additional/variant queries. ### Date Range Fallback All date-ranged queries above default to `LAST_30_DAYS`. If results are empty or near-zero (<$5 total spend): 1. Re-run with `DURING LAST_90_DAYS` 2. If still empty, re-run without a date filter (all-time) 3. **Always state the date range used** in the output header 4. **If fallback was needed**, note: "Account dormant — using [range] for historical context" This prevents reporting "no data" on paused or seasonal accounts that have useful history. ### Export Mode (no MCP) Ask the user for as much as they can provide: - Campaign overview (last 30 days) - Search Terms report (last 30 days, top 200+) - Conversion actions list - Current negative keyword list - RSA asset report (optional) - Any notes on tracking, recent changes, or known issues The audit works with partial data — it will note confidence limitations. --- ## Audit Order ### 1. Can we trust the account? If tracking is weak, say so immediately. Assign tracking confidence level (High/Medium/Low/Broken). Note which downstream conclusions are conditional on tracking trust. ### 2. What is the account learning about buyer intent? Summarize the query and intent reality. Build or update the Intent Map. Note where intent classes are well-routed vs. poorly routed. ### 3. Where is waste leaking spend? Identify junk, ambiguity, and routing failures. Quantify the waste. Separate "waste from bad queries" from "waste from bad structure." ### 4. What should be negative vs. isolated vs. already wrongly negated? Distinguish exclusion from structure problems. Default to isolation when in doubt. **Also review existing negatives for harm:** Check if any current negatives are blocking traffic that matches the business's services. Smart campaign auto-negatives and inherited negatives are common culprits. If harmful negatives are found, include a "Negatives to Remove" section in the negative draft. ### 5. What structure changes matter most? Focus on where meaning is being mixed. Prioritize by spend impact, not by cosmetic neatness. ### 6. Are RSAs aligned with buyer language? Check whether ads reflect real intent and LP promise. Note specific gaps between search language and ad copy. ### 7. What deserves more budget, less budget, or no touch yet? Make support/protect/reduce/hold decisions. **Gate budget scaling behind tracking confidence.** ### 8. Are landing pages converting (or do they look like they should be)? When conversion rates look suspiciously low, run a quick Fork A check (tracking) before blaming the landing page. If clicks are high but conversions are near-zero, the tag may not be firing — not the page's fault. For full landing page → conversion path diagnosis, delegate to `/google-ads landing-review`. See `skills/google-ads-landing-review/SKILL.md` and `google-ads/references/landing-page-playbook.md`. ### 9. Is PMax helping or contaminating signal? Be explicit about inference vs. direct evidence. Check for branded/high-intent cannibalization. ## Draft Output ### Prioritized Draft Batch The audit produces **multiple drafts** organized by priority. Not everything needs fixing at once. **Priority tiers:** 1. **Fix first** — tracking problems that gate everything else 2. **Quick wins** — clear negatives that stop obvious waste 3. **Strategic moves** — structure changes, budget reallocation 4. **Refinement** — RSA refreshes, minor optimizations For each tier with actionable findings, create a draft: - Tracking fixes → `drafts/templates/tracking-draft.md` → `workspace/ads/drafts/YYYY-MM-DD-[account-slug]-tracking-fix.md` - Negatives → `drafts/templates/negative-draft.md` → `workspace/ads/drafts/YYYY-MM-DD-[account-slug]-negatives.md` - Structure → `drafts/templates/structure-draft.md` → `workspace/ads/drafts/YYYY-MM-DD-[account-slug]-structure.md` - Budget → `drafts/templates/budget-draft.md` → `workspace/ads/drafts/YYYY-MM-DD-[account-slug]-budget-realloc.md` - RSAs → `drafts/templates/rsa-draft.md` → `workspace/ads/drafts/YYYY-MM-DD-[account-slug]-rsa-refresh.md` - Landing page → `drafts/templates/landing-review-draft.md` → `workspace/ads/drafts/YYYY-MM-DD-[account-slug]-landing-review.md` **Cross-reference dependencies between drafts.** E.g., "Apply tracking fix before budget scaling." Update `workspace/ads/drafts/_index.md` with all new drafts. If 2 or more drafts are created in the same audit run, also write `workspace/ads/drafts/_batch-YYYY-MM-DD-[account-slug].md` with: - Account, date range, and tracking confidence - Drafts created in that run grouped by priority tier - Dependency chain and recommended review order - Quick-apply candidates Do not list the `_batch-*.md` packet in `_index.md`. It is an audit packet, not a draft. ### Always update workspace memory: - `workspace/ads/intent-map.md` — build or update from search terms - `workspace/ads/findings.md` — audit findings log - `workspace/ads/negatives.md` — recommended negatives - `workspace/ads/learnings.md` — what we learned - `workspace/ads/account.md` — account context if this is first audit ## Output Shape 1. **Account Status block** — account name, CID, status (active/suspended/dormant), date range used, tracking confidence, mode 2. **Executive summary** — 3-5 sentences: biggest issues, tracking trust, top priority 3. Tracking confidence detail (High/Medium/Low/Broken) 4. Intent landscape (brief Intent Map) 5. Waste analysis (quantified) 6. Structure assessment 7. RSA alignment check 8. Budget posture 9. PMax assessment 10. **Prioritized action plan** — organized by tier 11. Drafts created (list with paths, summaries, and dependencies) 12. Confidence assessment 13. Memory updates ## Rules - Tracking trust comes first. Everything else is conditional. - Search behavior beats generic advice. - Distinguish symptoms from root causes. - Do not bury the highest-leverage decision. - Use benchmarks as context, not as authority. - **The audit is the highest-value skill in the system. It should produce the clearest, most actionable output. Every finding should either become a draft or be explicitly marked as "no action needed."** - **Prioritize ruthlessly. An audit that recommends 30 equally-weighted changes is useless. An audit that identifies the 3 things that matter most is gold.** --- # Generate schema markup for rich results Create valid JSON-LD structured data for Article, Product, FAQ, HowTo, Organization, and more. Maximize Google rich results eligibility. **Canonical:** https://www.marketingskills.sh/OpenClaudia/openclaudia-skills/schema-markup **Source:** https://github.com/OpenClaudia/openclaudia-skills/tree/main/skills/schema-markup **Category:** SEO **Score:** 4.5 **Tags:** schema-markup, structured-data, json-ld, rich-results, technical-seo, google-search --- name: schema-markup description: Generate Schema.org structured data (JSON-LD) for any page type. Use when the user says "schema markup", "structured data", "JSON-LD", "rich snippets", "rich results", "FAQ schema", "product schema", "article schema", "breadcrumb schema", "organization schema", "how-to schema", "review schema", or asks about structured data for SEO. --- # Schema Markup Generator Skill You are an expert in Schema.org structured data and Google's rich results requirements. Generate valid, complete JSON-LD markup that maximizes eligibility for Google rich results. ## Supported Schema Types This skill supports the following schema types. When the user asks for schema, determine which type(s) are appropriate based on the page content. ### 1. Article / BlogPosting / NewsArticle **Use for:** Blog posts, news articles, editorial content **Rich result:** Article carousel, headline in search ```json { "@context": "https://schema.org", "@type": "Article", "headline": "Your Article Title (max 110 characters)", "description": "Brief description of the article (max 160 characters)", "image": [ "https://example.com/image-16x9.jpg", "https://example.com/image-4x3.jpg", "https://example.com/image-1x1.jpg" ], "datePublished": "2025-01-15T08:00:00+00:00", "dateModified": "2025-01-20T10:30:00+00:00", "author": [{ "@type": "Person", "name": "Author Name", "url": "https://example.com/author/name", "jobTitle": "Senior Editor", "sameAs": [ "https://twitter.com/authorhandle", "https://linkedin.com/in/authorname" ] }], "publisher": { "@type": "Organization", "name": "Publisher Name", "logo": { "@type": "ImageObject", "url": "https://example.com/logo.png", "width": 600, "height": 60 } }, "mainEntityOfPage": { "@type": "WebPage", "@id": "https://example.com/article-url" }, "wordCount": 2500, "articleSection": "Technology", "keywords": ["keyword1", "keyword2", "keyword3"], "isAccessibleForFree": true } ``` **Google requirements:** - `headline` is required (max 110 characters) - `image` is required (provide 3 aspect ratios: 16:9, 4:3, 1:1; each > 696px wide) - `datePublished` is required (ISO 8601 format) - `author.name` is required - For `NewsArticle`, also add `dateline` if applicable - For `BlogPosting`, `@type` changes to `"BlogPosting"` --- ### 2. Product **Use for:** Product pages, e-commerce listings **Rich result:** Product snippet with price, availability, reviews ```json { "@context": "https://schema.org", "@type": "Product", "name": "Product Name", "description": "Product description", "image": [ "https://example.com/product-1.jpg", "https://example.com/product-2.jpg" ], "sku": "SKU-12345", "mpn": "MPN-67890", "gtin13": "0123456789012", "brand": { "@type": "Brand", "name": "Brand Name" }, "offers": { "@type": "Offer", "url": "https://example.com/product", "priceCurrency": "USD", "price": "99.99", "priceValidUntil": "2025-12-31", "availability": "https://schema.org/InStock", "itemCondition": "https://schema.org/NewCondition", "seller": { "@type": "Organization", "name": "Seller Name" }, "shippingDetails": { "@type": "OfferShippingDetails", "shippingRate": { "@type": "MonetaryAmount", "value": "0", "currency": "USD" }, "deliveryTime": { "@type": "ShippingDeliveryTime", "handlingTime": { "@type": "QuantitativeValue", "minValue": 0, "maxValue": 1, "unitCode": "DAY" }, "transitTime": { "@type": "QuantitativeValue", "minValue": 1, "maxValue": 5, "unitCode": "DAY" } }, "shippingDestination": { "@type": "DefinedRegion", "addressCountry": "US" } }, "hasMerchantReturnPolicy": { "@type": "MerchantReturnPolicy", "applicableCountry": "US", "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow", "merchantReturnDays": 30, "returnMethod": "https://schema.org/ReturnByMail", "returnFees": "https://schema.org/FreeReturn" } }, "aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.5", "bestRating": "5", "ratingCount": "142" }, "review": [{ "@type": "Review", "author": { "@type": "Person", "name": "Reviewer Name" }, "datePublished": "2025-01-10", "reviewBody": "Review text here", "reviewRating": { "@type": "Rating", "ratingValue": "5", "bestRating": "5" } }] } ``` **Google requirements:** - `name` is required - `offers`, `review`, or `aggregateRating` - at least one required - `offers.price` and `offers.priceCurrency` required if offers present - `offers.availability` must use Schema.org enum values - As of 2024, `shippingDetails` and `hasMerchantReturnPolicy` are recommended for merchant listings --- ### 3. FAQPage **Use for:** FAQ sections, Q&A pages **Rich result:** Expandable FAQ in search results ```json { "@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [ { "@type": "Question", "name": "What is the first question?", "acceptedAnswer": { "@type": "Answer", "text": "

The answer with HTML formatting allowed. You can include links.

" } }, { "@type": "Question", "name": "What is the second question?", "acceptedAnswer": { "@type": "Answer", "text": "Plain text answers also work." } } ] } ``` **Google requirements:** - Each `Question` must have exactly one `acceptedAnswer` - Answer `text` can include HTML: `

` through `

`, `
`, `
    `, `