agentleFS
Fei2-LabsFei2-Labs/skill-genie/skills/landing-launch-check/SKILL.md

landing-launch-check

Skill12 starsChanged 50 days agoMITView on GitHub

Final pre-launch audit for a multilingual marketing landing page — SEO, AEO/AIO (structured data for AI search), GEO, and EU compliance (GDPR/ePrivacy). Catches the common failures that ship to production when a landing page is built on top of a shared app layer.

The file

We split this skill into the rules it gives an agent

8 rules. About 0 are common, 7 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈7

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Before submitting to Google Search Console and AI search engines

    line 18
  2. Distinctive:

    Test at https://search.google.com/test/rich-results

    line 128
  3. Distinctive:

    Test at https://validator.schema.org/

    line 129
  4. Distinctive:

    Pre-consent tag firing: no third-party trackers load before consent is given

    line 205
  5. Distinctive:

    Low risk: No third-party trackers, first-party cookies only, no forms → compliance is mostly about having the pages present

    line 233
  6. Distinctive:

    If the user wants to submit the sitemap to Google Search Console using their already-logged-in Chrome:

    line 239
  7. Very specific:

    Ensure Chrome is running with --remote-debugging-port=9222

    line 241
  8. Distinctive:

    Verify the sitemap appears in the list with status "Success"

    line 246

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Landing Page Final Launch Check

A focused, evidence-based audit that runs after the landing page is built and deployed, before declaring it launch-ready. Designed for marketing landing pages that extend a shared app layer (e.g. a Nuxt app-base with i18n, auth, and compliance components inherited by B2C/B2B apps).

When to use

  • A new landing page has been deployed and needs a final sign-off
  • A landing page was built on top of a shared app layer and may have inherited wrong defaults
  • The site targets multiple locales (e.g. Swedish + English) and needs per-locale SEO
  • The site targets EU users and needs GDPR/ePrivacy compliance
  • Before submitting to Google Search Console and AI search engines

What this skill checks

Four dimensions, each with concrete pass/fail criteria:

  1. Technical SEO — meta tags, canonical, hreflang, sitemap, robots.txt
  2. AEO/AIO/GEO — structured data (JSON-LD) for AI search engines and rich results
  3. i18n correctness — per-locale meta, lang attribute, og:locale
  4. EU compliance — cookie consent, privacy policy, terms, GDPR Art. 13 elements

Prerequisites

  • The landing page is deployed and accessible via its public URL
  • curl available for HTTP inspection
  • Python 3 for parsing HTML
  • (Optional) Playwright + a Chrome instance with CDP for Google Search Console submission
  • (Optional) gh CLI for creating follow-up issues

Audit procedure

Phase 1: Crawl the live pages

Fetch the HTML for every locale variant and the robots.txt:

curl -s https://example.com/ -o /tmp/landing-sv.html
curl -s https://example.com/en -o /tmp/landing-en.html
curl -s https://example.com/robots.txt -o /tmp/robots.txt
curl -s -o /dev/null -w "%{http_code}" https://example.com/sitemap.xml

Phase 2: Technical SEO audit

For each locale variant, extract and check:

import re
html = open('/tmp/landing-sv.html').read()
checks = {
    'title': re.findall(r'<title>([^<]*)</title>', html),
    'lang': re.findall(r'<html[^>]*lang="([^"]*)"', html),
    'description': re.findall(r'name="description" content="([^"]{0,80})', html)[:1],
    'canonical': re.findall(r'rel="canonical" href="([^"]*)"', html),
    'hreflang': re.findall(r'hreflang="([^"]*)"', html),
    'og:locale': re.findall(r'og:locale" content="([^"]*)"', html),
    'og:image': re.findall(r'og:image" content="([^"]{0,90})', html),
    'og:url': re.findall(r'og:url" content="([^"]*)"', html),
    'twitter:card': re.findall(r'twitter:card" content="([^"]*)"', html),
    'json-ld': len(re.findall(r'application/ld\+json', html)),
    'robots_meta': re.findall(r'<meta name="robots" content="([^"]*)"', html),
}

Pass criteria (per locale):

CheckRequirementCommon failure
titlePresent, localized, < 60 charsHardcoded English on all locales
langMatches locale (sv-SE, en-GB)Inherited lang="sv" on EN page
descriptionPresent, localized, 150-160 charsHardcoded fallback from nuxt.config
canonicalSelf-referencing, per-locale URLMissing entirely
hreflangx-default + all locale variantsMissing (no i18n.baseUrl set)
og:localeMatches locale (sv_SE, en_GB)Missing
og:imagePoints to production domain, not stagingStaging/dev domain leaked into prod
og:urlPer-locale URLSingle hardcoded URL
twitter:cardsummary_large_imageMissing
json-ldAt least 1 blockZero structured data

robots.txt checks:

User-Agent: *
Allow: /
Disallow: /login          # inherited auth pages shouldn't be indexed
Disallow: /register
Disallow: /en/login
Sitemap: https://example.com/sitemap.xml

Sitemap checks:

  • /sitemap.xml returns 200
  • Contains all public URLs
  • Has xhtml:link rel="alternate" with hreflang for locale variants

Phase 3: AEO/AIO/GEO audit (structured data)

AI search engines (Google AI Overviews, Perplexity, ChatGPT Search, Bing Copilot) rely on structured data to cite sources. Check for JSON-LD presence and validity:

import json, re
html = open('/tmp/landing-sv.html').read()
m = re.search(r'<script type="application/ld\+json">(.*?)</script>', html, re.DOTALL)
if m:
    data = json.loads(m.group(1))
    types = [g['@type'] for g in data.get('@graph', [data])]
    print('JSON-LD types:', types)

Recommended JSON-LD types for a landing page:

TypePurposeRequired fields
OrganizationBrand entity for AI/Knowledge Graphname, url, logo
WebSiteSite entity with languagename, url, inLanguage, publisher
FAQPageQ&A pairs for rich results + AI citationmainEntity[].name, acceptedAnswer.text
SportsEvent(if applicable) Event listingsname, startDate, location, competitor

Validation:

Key AEO insight: The FAQ content must be localized per locale — an EN page serving SV FAQ text confuses AI engines. Use i18n keys in the JSON-LD generator, not hardcoded strings.

Phase 4: i18n correctness audit

The most common landing page bug: a shared app layer hardcodes one locale's meta in nuxt.config.ts or definePageMeta(), and every locale variant serves the same meta.

Check each locale variant independently:

# SV page should have sv-SE lang, sv_SE og:locale, Swedish title
curl -s https://example.com/ | grep -oP '(?<=lang=")[^"]*|(?<=og:locale" content=")[^"]*|(?<=<title>)[^<]*'

# EN page should have en-GB lang, en_GB og:locale, English title
curl -s https://example.com/en | grep -oP '(?<=lang=")[^"]*|(?<=og:locale" content=")[^"]*|(?<=<title>)[^<]*'

Common failures when extending a shared app layer:

  1. app.head.htmlAttrs.lang hardcoded in nuxt.config.ts → all locales get same lang
  2. definePageMeta({ title: '...' }) hardcoded → all locales get same title
  3. useSeoMeta({ description: '...' }) hardcoded → all locales get same description
  4. og:image URL points to staging domain → leaked from default config
  5. No i18n.baseUrl → useLocaleHead() can't generate canonical/hreflang

Fix pattern (Nuxt + @nuxtjs/i18n):

// In the page component, NOT in nuxt.config.ts
const { t, locale } = useI18n()
const i18nHead = useLocaleHead({ seo: true, lang: true })

useHead({
  htmlAttrs: computed(() => i18nHead.value.htmlAttrs ?? {}),
  link: computed(() => i18nHead.value.link ?? []),
  meta: computed(() => i18nHead.value.meta ?? []),
})

useSeoMeta({
  title: () => t('landing.meta.title'),
  description: () => t('landing.meta.description'),
  ogLocale: () => locale.value === 'sv' ? 'sv_SE' : 'en_GB',
  // ... all meta from i18n keys, not hardcoded
})
// nuxt.config.ts — set baseUrl so useLocaleHead can generate canonical/hreflang
i18n: {
  baseUrl: 'https://example.com',
}

Phase 5: EU compliance audit

Business classification (do this first):

  • Is it B2C or B2B?
  • Does it process personal data? (cookies, analytics, forms, auth)
  • Does it target EU/EEA users? (GDPR + ePrivacy apply)
  • Does it have third-party trackers? (GA, Meta Pixel, Hotjar, etc.)

Required pages check:

for p in privacy terms cookie-settings; do
  echo -n "/$p: "; curl -s -o /dev/null -w "%{http_code}" https://example.com/$p
done

Cookie consent banner check:

  1. Banner appears on first visit (no prior consent cookie)
  2. "Accept All" and "Essential Only" buttons are equally prominent (no dark pattern)
  3. Consent is recorded with a timestamp
  4. Consent can be withdrawn via /cookie-settings
  5. Pre-consent tag firing: no third-party trackers load before consent is given

Privacy policy GDPR Art. 13 elements:

Scan the privacy policy text for these required elements:

ElementSwedish keywordStatus
Data controller identitypersonuppgiftsansvarig
Legal basisrättslig grund
Retention periodlagringstid
Data subject rightsrättigheter
Supervisory authority (IMY for SE)IMY
Third-party processorstredje part / underbehandlare
Data transfer outside EU/EEAöverföring
Contact for data protectione-post / kontakt
import re, html as H
h = open('/tmp/priv.html').read()
text = H.unescape(re.sub(r'\s+', ' ', re.sub(r'<[^>]+>', ' ', re.sub(r'<script.*?</script>', '', h, flags=re.DOTALL))))
kws = ['personuppgiftsansvarig', 'rättslig grund', 'lagringstid', 'rättigheter', 'IMY', 'tredje', 'överföring', 'e-post']
for k in kws:
    print(f'{k}: {k.lower() in text.lower()}')

Risk classification:

  • Low risk: No third-party trackers, first-party cookies only, no forms → compliance is mostly about having the pages present
  • Medium risk: Analytics with consent gating, auth/login collecting email + name
  • High risk: Third-party trackers, payment processing, cross-border data transfer, children's data → legal review required

Phase 6: Submit to Google Search Console (optional, via CDP)

If the user wants to submit the sitemap to Google Search Console using their already-logged-in Chrome:

  1. Ensure Chrome is running with --remote-debugging-port=9222
  2. Connect via Playwright connectOverCDP
  3. Navigate to the Sitemaps page
  4. Fill the sitemap input with the full URL (e.g. https://example.com/sitemap.xml, not just sitemap.xml)
  5. Click the Submit button (Material Design div.U26fgk.O0WRkf, not a native <button>)
  6. Verify the sitemap appears in the list with status "Success"
import { chromium } from 'playwright'
const browser = await chromium.connectOverCDP('http://127.0.0.1:9222')
const ctx = browser.contexts()[0]
const page = await ctx.newPage()
await page.goto('https://search.google.com/search-console/sitemaps?resource_id=sc-domain:example.com', { waitUntil: 'networkidle' })
await page.waitForTimeout(3000)
await page.locator('input[aria-label="Enter sitemap URL"]').fill('https://example.com/sitemap.xml')
await page.locator('div.U26fgb.O0WRkf').filter({ hasText: 'Submit' }).first().click()
await page.waitForTimeout(5000)
// Verify: look for "Success" in the sitemap table row

Gotcha: For sc-domain: properties, the sitemap URL must be the full HTTPS URL, not a relative path. Relative paths like sitemap.xml will return "Invalid sitemap address".

Phase 7: Generate report

Output a structured report with:

  1. Summary table — dimension × status (Pass/Fail/Warning)
  2. Findings — each issue with severity, evidence, and fix
  3. Remediation checklist — actionable items, ordered by severity
  4. Compliance disclaimer — "This audit is not legal advice. High-risk businesses should have final materials reviewed by qualified counsel."

Severity levels

LevelMeaningAction
🔴 CriticalBreaks indexing, leaks wrong locale, or violates GDPRFix before launch
🟠 HighMisses SEO/AEO opportunity or compliance gapFix before launch
🟡 MediumSuboptimal but not breakingFix after launch
🟢 LowCosmetic or future improvementBacklog

Common failure patterns (from real audits)

  1. SV page serves EN meta — nuxt.config.ts hardcodes English title/description; the SV page (default locale) serves English to Google.se
  2. EN page has lang="sv" — app.head.htmlAttrs.lang hardcoded in shared config; useLocaleHead() not used
  3. No canonical URLs — i18n.baseUrl not set; useLocaleHead() can't generate them
  4. No hreflang alternates — same cause as above; Google can't associate locale variants
  5. og:image points to staging — default config leaked from dev environment
  6. Zero structured data — no JSON-LD at all; AI search engines have no structured signal
  7. Inherited auth pages indexable — /login, /register from shared app layer not disallowed in robots.txt
  8. No sitemap.xml — static sitemap not created; Google has to discover URLs by crawling
  9. Privacy policy missing GDPR Art. 13 elements — controller identity, legal basis, retention period, IMY complaints right not mentioned
  10. Sitemap submitted as relative path — Google Search Console rejects sitemap.xml; needs full https://example.com/sitemap.xml

Tools used

  • curl — fetch live HTML and check HTTP status
  • python3 with re — parse HTML meta tags and JSON-LD
  • playwright (optional) — submit sitemap to Google Search Console via CDP
  • gh CLI (optional) — create follow-up issues for remediation items

Disclaimer

This audit provides technical SEO checking, structured data validation, and compliance issue-spotting only. It is not legal advice. High-risk, regulated, or cross-border businesses should have final compliance materials reviewed by qualified counsel.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.