@softspark/ai-toolkit 2.4.1 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/AGENTS.md +32 -19
  2. package/CHANGELOG.md +23 -0
  3. package/README.md +11 -11
  4. package/app/.claude-plugin/plugin.json +1 -1
  5. package/app/agents/code-reviewer.md +6 -7
  6. package/app/agents/frontend-specialist.md +33 -2
  7. package/app/agents/seo-specialist.md +1 -1
  8. package/app/personas/frontend-lead.md +48 -5
  9. package/app/skills/a11y-validate/SKILL.md +377 -0
  10. package/app/skills/a11y-validate/reference/aria-patterns.md +259 -0
  11. package/app/skills/a11y-validate/reference/eaa-compliance.md +252 -0
  12. package/app/skills/a11y-validate/reference/mobile-eaa.md +329 -0
  13. package/app/skills/a11y-validate/reference/wcag-2-1-aa.md +285 -0
  14. package/app/skills/a11y-validate/reference/wcag-2-2-aa.md +221 -0
  15. package/app/skills/a11y-validate/scripts/a11y-scanner.py +639 -0
  16. package/app/skills/clean-code/reference/python.md +3 -3
  17. package/app/skills/design-engineering/SKILL.md +2 -5
  18. package/app/skills/review/SKILL.md +30 -6
  19. package/app/skills/seo-validate/SKILL.md +460 -0
  20. package/app/skills/seo-validate/reference/core-web-vitals.md +445 -0
  21. package/app/skills/seo-validate/reference/geo-aeo-patterns.md +259 -0
  22. package/app/skills/seo-validate/reference/geo-guidelines.md +248 -0
  23. package/app/skills/seo-validate/reference/schema-types.md +465 -0
  24. package/app/skills/seo-validate/reference/spa-ssg-patterns.md +351 -0
  25. package/app/skills/seo-validate/reference/w3c-guidelines.md +289 -0
  26. package/app/skills/seo-validate/scripts/seo-scanner.py +549 -0
  27. package/bin/ai-toolkit.js +4 -4
  28. package/kb/reference/architecture-overview.md +1 -1
  29. package/kb/reference/comparison.md +1 -1
  30. package/kb/reference/skills-catalog.md +3 -1
  31. package/llms-full.txt +10 -6
  32. package/manifest.json +3 -3
  33. package/package.json +2 -2
  34. package/scripts/config_cli.py +4 -10
  35. package/scripts/doctor.py +3 -3
  36. package/scripts/install_steps/ai_tools.py +1 -1
  37. package/scripts/plugin.py +1 -1
@@ -0,0 +1,445 @@
1
+ # Core Web Vitals Reference
2
+
3
+ Reference for `seo-validate` Category 5. Static-analysis patterns that cause LCP/INP/CLS regressions, plus resource hint guidance and above-the-fold heuristics.
4
+
5
+ Source: web.dev Core Web Vitals, HTML Living Standard `<link>` rel types, W3C Resource Hints, per-framework image component documentation.
6
+
7
+ ## Thresholds
8
+
9
+ | Metric | Good | Needs Improvement | Poor |
10
+ |--------|------|-------------------|------|
11
+ | LCP (Largest Contentful Paint) | <2.5s | 2.5–4.0s | >4.0s |
12
+ | INP (Interaction to Next Paint) | <200ms | 200–500ms | >500ms |
13
+ | CLS (Cumulative Layout Shift) | <0.1 | 0.1–0.25 | >0.25 |
14
+
15
+ 75th-percentile mobile values count for ranking. The skill can't measure these at runtime — it flags code patterns known to cause regressions.
16
+
17
+ ---
18
+
19
+ ## LCP (Largest Contentful Paint)
20
+
21
+ The LCP element is typically the hero image or H1 text in the first viewport. Goal: render it as fast as possible.
22
+
23
+ ### Image dimensions
24
+
25
+ Every `<img>` and `<video>` must declare `width` and `height` (or `aspect-ratio` CSS). Missing dimensions cause:
26
+ - CLS as the image loads.
27
+ - LCP delay (browser can't reserve space / estimate priority).
28
+
29
+ ```html
30
+ <!-- Good -->
31
+ <img src="hero.jpg" width="1200" height="630" alt="...">
32
+
33
+ <!-- Bad (CLS + LCP) -->
34
+ <img src="hero.jpg" alt="...">
35
+ ```
36
+
37
+ ### fetchpriority
38
+
39
+ `fetchpriority="high"` on the LCP image tells the browser to fetch it before other resources. Introduced in Chrome 101 (May 2022).
40
+
41
+ ```html
42
+ <img src="hero.jpg" width="1200" height="630" fetchpriority="high" alt="...">
43
+ ```
44
+
45
+ `fetchpriority="low"` on below-the-fold images frees bandwidth for critical resources.
46
+
47
+ ### loading="lazy" on LCP = BAD
48
+
49
+ `loading="lazy"` delays the image load until it's near the viewport. Applied to the LCP element, this is a HIGH-severity anti-pattern.
50
+
51
+ Only apply `loading="lazy"` to images **below the fold**.
52
+
53
+ ```html
54
+ <!-- Bad: LCP element marked lazy -->
55
+ <img src="hero.jpg" loading="lazy" alt="Hero">
56
+
57
+ <!-- Good: LCP element eager + high priority -->
58
+ <img src="hero.jpg" fetchpriority="high" alt="Hero">
59
+
60
+ <!-- Good: below-the-fold image lazy -->
61
+ <img src="testimonial.jpg" loading="lazy" decoding="async" alt="Jane, happy customer">
62
+ ```
63
+
64
+ ### decoding="async"
65
+
66
+ Signals that image decoding can happen off the main thread. Apply to non-critical images.
67
+
68
+ ```html
69
+ <img src="sidebar.jpg" loading="lazy" decoding="async" alt="...">
70
+ ```
71
+
72
+ Don't apply `decoding="async"` to the LCP image — it can delay paint. Use `decoding="sync"` or omit.
73
+
74
+ ### Preloading the LCP image
75
+
76
+ When the LCP image URL is known at build time (hero background, article cover), preload it:
77
+
78
+ ```html
79
+ <link rel="preload" as="image" href="/images/hero.jpg"
80
+ imagesrcset="/images/hero-640.jpg 640w, /images/hero-1280.jpg 1280w"
81
+ imagesizes="100vw">
82
+ ```
83
+
84
+ ### Font loading
85
+
86
+ Fonts frequently gate LCP when text is the LCP element.
87
+
88
+ ```css
89
+ /* Good: text renders in fallback, swaps when webfont loads */
90
+ @font-face {
91
+ font-family: 'Inter';
92
+ src: url('/fonts/inter.woff2') format('woff2');
93
+ font-display: swap; /* or 'optional' for aggressive LCP optimization */
94
+ }
95
+
96
+ /* Bad: FOIT (Flash of Invisible Text) blocks paint */
97
+ @font-face {
98
+ font-family: 'Inter';
99
+ src: url('/fonts/inter.woff2') format('woff2');
100
+ /* font-display defaults to 'auto', which most browsers treat as 'block' = FOIT */
101
+ }
102
+ ```
103
+
104
+ Preload webfonts used above the fold:
105
+
106
+ ```html
107
+ <link rel="preload" as="font" type="font/woff2"
108
+ href="/fonts/inter-var.woff2" crossorigin>
109
+ ```
110
+
111
+ The `crossorigin` attribute is required even for same-origin fonts — fonts are always fetched with CORS.
112
+
113
+ ### Responsive images
114
+
115
+ Images wider than ~600px should use `srcset` + `sizes` or `<picture>`:
116
+
117
+ ```html
118
+ <img
119
+ src="hero-1280.jpg"
120
+ srcset="hero-640.jpg 640w, hero-1280.jpg 1280w, hero-2560.jpg 2560w"
121
+ sizes="(max-width: 768px) 100vw, 50vw"
122
+ width="1280" height="720" alt="...">
123
+ ```
124
+
125
+ Over-fetching a 2560px image to a 375px mobile viewport wastes bytes and delays LCP.
126
+
127
+ ---
128
+
129
+ ## INP (Interaction to Next Paint)
130
+
131
+ Replaces FID (First Input Delay) as the CWV interaction metric in March 2024. Measures longest interaction latency across the session.
132
+
133
+ ### Render-blocking scripts
134
+
135
+ Scripts in `<head>` without `async` or `defer` block parsing:
136
+
137
+ ```html
138
+ <!-- Bad: blocks parser + CSSOM -->
139
+ <script src="/analytics.js"></script>
140
+
141
+ <!-- Good: doesn't block parser, runs after parse but before DOMContentLoaded -->
142
+ <script defer src="/app.js"></script>
143
+
144
+ <!-- Good: downloads in parallel, runs ASAP (order not guaranteed) -->
145
+ <script async src="/analytics.js"></script>
146
+ ```
147
+
148
+ ### Third-party scripts
149
+
150
+ Analytics, chat widgets, ads, A/B test SDKs commonly cause INP regressions.
151
+
152
+ Patterns to flag:
153
+ - `<script src="https://www.googletagmanager.com/...">` without `async`
154
+ - Intercom, Drift, HubSpot widgets loaded synchronously
155
+ - Ads via direct `<script>` (most use `async` by default, verify)
156
+
157
+ Next.js `<Script>` component with strategy:
158
+ ```jsx
159
+ <Script src="https://analytics.example.com" strategy="lazyOnload" />
160
+ ```
161
+
162
+ Strategies:
163
+ - `beforeInteractive` — blocks hydration, rarely needed.
164
+ - `afterInteractive` (default) — loads after hydration.
165
+ - `lazyOnload` — loads during idle time. Best for analytics/chat.
166
+ - `worker` — runs in Web Worker (Partytown). Experimental.
167
+
168
+ ### document.write
169
+
170
+ Always HIGH. Modern browsers disable `document.write` for scripts loaded over 2G-like networks. Never use.
171
+
172
+ ### Hydration cost
173
+
174
+ Heavy synchronous work during hydration blocks INP. Red flags:
175
+
176
+ ```jsx
177
+ useEffect(() => {
178
+ // Large sync block — splits Long Tasks
179
+ heavyComputation();
180
+ anotherHeavyFunction();
181
+ yetAnotherOne();
182
+ }, []);
183
+ ```
184
+
185
+ Mitigation: `requestIdleCallback`, `scheduler.postTask`, or split across effects.
186
+
187
+ ### Bundle size
188
+
189
+ Bundles >300KB gzipped gating interaction = INP risk. Check:
190
+ - Imports of entire lodash / moment / date-fns (use tree-shaking or day.js)
191
+ - Icon libraries imported wholesale (import only what's used)
192
+ - PDF/charting libraries loaded eagerly (dynamic import)
193
+
194
+ ---
195
+
196
+ ## CLS (Cumulative Layout Shift)
197
+
198
+ ### Image/video/iframe dimensions
199
+
200
+ Covered above — always set `width`/`height` or `aspect-ratio`.
201
+
202
+ ### Iframes
203
+
204
+ YouTube embeds, maps, Twitter embeds cause CLS without reserved space:
205
+
206
+ ```html
207
+ <!-- Bad -->
208
+ <iframe src="https://www.youtube.com/embed/..."></iframe>
209
+
210
+ <!-- Good: fixed container -->
211
+ <div style="aspect-ratio: 16/9;">
212
+ <iframe src="..." width="100%" height="100%"></iframe>
213
+ </div>
214
+ ```
215
+
216
+ ### Dynamic content
217
+
218
+ Ads, embeds, and "related content" widgets injected after paint cause CLS. Reserve space with CSS:
219
+
220
+ ```css
221
+ .ad-slot {
222
+ min-height: 250px; /* Or aspect-ratio tied to ad size */
223
+ }
224
+ ```
225
+
226
+ ### Font swap shifts
227
+
228
+ `font-display: swap` causes a visible shift when the webfont loads (FOUT — Flash of Unstyled Text). Minimize by:
229
+ - Using system-ui fonts as fallback with matching metrics (`size-adjust`, `ascent-override`).
230
+ - Using `font-display: optional` (accepts missing-font in exchange for zero shift).
231
+
232
+ ### Hydration mismatches
233
+
234
+ Server renders one thing, client renders another — content "flashes" or "jumps":
235
+
236
+ ```jsx
237
+ // Bad — hydration mismatch potential
238
+ function Component() {
239
+ if (typeof window !== 'undefined') {
240
+ return <ClientOnlyContent />;
241
+ }
242
+ return <ServerContent />;
243
+ }
244
+
245
+ // Good — use useEffect with loading state
246
+ function Component() {
247
+ const [mounted, setMounted] = useState(false);
248
+ useEffect(() => setMounted(true), []);
249
+ return mounted ? <ClientOnlyContent /> : <Skeleton />;
250
+ }
251
+ ```
252
+
253
+ ---
254
+
255
+ ## Resource Hints
256
+
257
+ HTML Living Standard + W3C Resource Hints. Hints are suggestions — browsers can ignore them.
258
+
259
+ ### preload
260
+
261
+ High-priority fetch for critical, known resources.
262
+
263
+ ```html
264
+ <link rel="preload" as="image" href="/hero.jpg">
265
+ <link rel="preload" as="font" type="font/woff2" href="/fonts/inter.woff2" crossorigin>
266
+ <link rel="preload" as="style" href="/critical.css">
267
+ <link rel="preload" as="script" href="/critical.js">
268
+ ```
269
+
270
+ Rules:
271
+ - Must include `as` attribute matching the resource type.
272
+ - Must appear BEFORE the resource uses it in document order.
273
+ - Don't over-use — >6 preloads starts to hurt.
274
+ - Preloaded resource not used within a few seconds → browser console warning.
275
+
276
+ ### prefetch
277
+
278
+ Low-priority fetch for probable-next-navigation resources.
279
+
280
+ ```html
281
+ <link rel="prefetch" href="/next-page.js">
282
+ ```
283
+
284
+ Framework-integrated prefetching:
285
+ - Next.js `<Link>` auto-prefetches visible links in production.
286
+ - Nuxt `<NuxtLink>` with `prefetch` prop.
287
+ - SvelteKit: `data-sveltekit-preload-data="hover"` or `"tap"`.
288
+ - Remix: `<Link prefetch="intent">`.
289
+
290
+ ### preconnect
291
+
292
+ Establishes early connection (DNS + TCP + TLS) to a 3rd-party origin.
293
+
294
+ ```html
295
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
296
+ <link rel="preconnect" href="https://www.google-analytics.com">
297
+ ```
298
+
299
+ Use for critical 3rd-party origins: font hosts, CDNs, analytics. Overuse contends with critical-path connections — limit to 3–4.
300
+
301
+ ### dns-prefetch
302
+
303
+ Lightweight DNS-only resolution. Use for less-critical origins or as fallback for browsers without preconnect support.
304
+
305
+ ```html
306
+ <link rel="dns-prefetch" href="https://some-analytics.example.com">
307
+ ```
308
+
309
+ ### modulepreload
310
+
311
+ Preload ESM modules on the critical path (bypasses discovery cost for import graphs).
312
+
313
+ ```html
314
+ <link rel="modulepreload" href="/build/chunk-main.js">
315
+ ```
316
+
317
+ Build tools can generate these automatically (Vite, esbuild, Rollup plugins).
318
+
319
+ ### Ordering
320
+
321
+ Preload/preconnect MUST appear before the resource that uses them — typically at the top of `<head>`, right after charset/viewport.
322
+
323
+ ```html
324
+ <head>
325
+ <meta charset="utf-8">
326
+ <meta name="viewport" content="...">
327
+
328
+ <!-- Resource hints first -->
329
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
330
+ <link rel="preload" as="image" href="/hero.jpg" fetchpriority="high">
331
+ <link rel="preload" as="font" type="font/woff2" href="/fonts/inter.woff2" crossorigin>
332
+
333
+ <!-- Then the resource (stylesheets) -->
334
+ <link rel="stylesheet" href="/styles.css">
335
+
336
+ <!-- Metadata -->
337
+ <title>...</title>
338
+ <meta name="description" content="...">
339
+ </head>
340
+ ```
341
+
342
+ ### Anti-patterns
343
+
344
+ - Preloading non-critical resources (every icon, every font weight).
345
+ - Preload without `as` attribute — browser issues warning, may not prioritize.
346
+ - Multiple `preload`s for images that aren't the LCP element.
347
+ - Preload after stylesheets/scripts that already request the resource.
348
+
349
+ ---
350
+
351
+ ## Above-the-Fold Heuristics
352
+
353
+ True "above-the-fold" detection requires rendering. In static analysis, treat these as ATF candidates:
354
+
355
+ 1. **First `<img>` / `<Image>` / `<NuxtImg>` / `<GatsbyImage>` / `<Image from 'astro:assets'>`** in a route/page component.
356
+ 2. **First child of `<main>`**, `<section>`, or `<header>`.
357
+ 3. **Components named** `Hero`, `HeroSection`, `Banner`, `Masthead`, `Jumbotron`, `CoverImage`, `SplashImage`, `HeaderImage`.
358
+ 4. **`<Image>` with `priority` prop** (Next.js) — developer has already declared ATF intent.
359
+
360
+ For ATF elements, required:
361
+ - `width` + `height` declared.
362
+ - `fetchpriority="high"` (or framework `priority` equivalent).
363
+ - NO `loading="lazy"`.
364
+ - Matching `<link rel="preload">` if URL known at build.
365
+
366
+ For below-the-fold images, recommended:
367
+ - `loading="lazy"`.
368
+ - `decoding="async"`.
369
+ - `fetchpriority="low"` (optional).
370
+
371
+ ---
372
+
373
+ ## Framework-Specific Image Components
374
+
375
+ ### Next.js — `next/image`
376
+
377
+ ```jsx
378
+ import Image from 'next/image';
379
+
380
+ // LCP image
381
+ <Image src="/hero.jpg" alt="..." width={1200} height={630} priority />
382
+
383
+ // Below fold
384
+ <Image src="/thumb.jpg" alt="..." width={400} height={300} loading="lazy" />
385
+ ```
386
+
387
+ Benefits: automatic WebP/AVIF, responsive srcset, lazy by default, supports `fill` + `sizes`.
388
+
389
+ Flags:
390
+ - `<img>` used instead of `<Image>` in route components → WARN (unless explicit raw HTML is needed).
391
+ - `<Image>` without `priority` on LCP element → HIGH.
392
+ - `<Image>` without `sizes` when using `fill` → WARN.
393
+
394
+ ### Nuxt — `<NuxtImg>` / `<NuxtPicture>`
395
+
396
+ ```vue
397
+ <NuxtImg src="/hero.jpg" width="1200" height="630" preload />
398
+ <NuxtImg src="/thumb.jpg" width="400" height="300" loading="lazy" />
399
+ ```
400
+
401
+ Requires `@nuxt/image` module.
402
+
403
+ ### Astro — `<Image>` from `astro:assets`
404
+
405
+ ```astro
406
+ ---
407
+ import { Image } from 'astro:assets';
408
+ import hero from '../assets/hero.jpg';
409
+ ---
410
+ <Image src={hero} alt="..." width={1200} height={630} loading="eager" />
411
+ ```
412
+
413
+ ### Gatsby — `GatsbyImage`
414
+
415
+ ```jsx
416
+ import { GatsbyImage, getImage } from 'gatsby-plugin-image';
417
+
418
+ const image = getImage(data.file);
419
+ <GatsbyImage image={image} alt="..." loading="eager" />
420
+ ```
421
+
422
+ ### SvelteKit — `@sveltejs/enhanced-img`
423
+
424
+ ```svelte
425
+ <script>
426
+ import heroImg from '$lib/images/hero.jpg?enhanced';
427
+ </script>
428
+ <enhanced:img src={heroImg} alt="..." />
429
+ ```
430
+
431
+ ---
432
+
433
+ ## References
434
+
435
+ - web.dev LCP: https://web.dev/lcp/
436
+ - web.dev INP: https://web.dev/inp/
437
+ - web.dev CLS: https://web.dev/cls/
438
+ - Resource Hints: https://www.w3.org/TR/resource-hints/
439
+ - HTML Living Standard `<link>` rel: https://html.spec.whatwg.org/multipage/links.html#linkTypes
440
+ - fetchpriority: https://web.dev/fetch-priority/
441
+ - font-display: https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display
442
+ - Next.js Image: https://nextjs.org/docs/app/api-reference/components/image
443
+ - Nuxt Image: https://image.nuxt.com/
444
+ - Astro Image: https://docs.astro.build/en/guides/images/
445
+ - Gatsby Image: https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image/
@@ -0,0 +1,259 @@
1
+ # AEO — Answer Engine Optimization
2
+
3
+ Reference for `seo-validate` Category 6 (GEO/AEO). Companion to [geo-guidelines.md](geo-guidelines.md). Focuses on optimizing content to appear in AI-generated answers — not just ranked pages.
4
+
5
+ All AEO findings are severity `INFO` — emerging practice, no known penalties.
6
+
7
+ ---
8
+
9
+ ## 1. What is AEO
10
+
11
+ Traditional SEO ranks pages. AEO targets AI-synthesized answers that cite or paraphrase your content without requiring a click.
12
+
13
+ **Key answer engines:**
14
+
15
+ | Engine | Mechanism |
16
+ |--------|-----------|
17
+ | Google AI Overviews | Retrieval-augmented summarization over organic index |
18
+ | Bing Copilot | GPT-4 grounded on Bing index; inline citations |
19
+ | Perplexity | Real-time retrieval + LLM synthesis; explicit source cards |
20
+ | ChatGPT Search | OpenAI web retrieval; used in ChatGPT Plus |
21
+
22
+ **Difference from classic SEO:** AI engines do not rank pages against each other — they extract the most useful fragment. A page ranked #8 with a direct-answer paragraph can beat #1 in AI responses.
23
+
24
+ ---
25
+
26
+ ## 2. Content Structure for AI Consumption
27
+
28
+ ### Direct-answer paragraphs (first-sentence-answer pattern)
29
+
30
+ AI engines extract the first 1–3 sentences after a heading as the answer to the implied question. Put the fact first.
31
+
32
+ ```html
33
+ <!-- Good -->
34
+ <h2>What is a canonical URL?</h2>
35
+ <p>A canonical URL is the preferred page version search engines should index when near-duplicates exist. Specify it with <code>&lt;link rel="canonical" href="..."&gt;</code> in <code>&lt;head&gt;</code>.</p>
36
+
37
+ <!-- Bad: answer buried after filler -->
38
+ <h2>What is a canonical URL?</h2>
39
+ <p>When building websites, you often encounter situations where multiple URLs serve similar content...</p>
40
+ ```
41
+
42
+ ### FAQ schema with concise Q&A pairs
43
+
44
+ `FAQPage` is the highest-precision signal. Each `acceptedAnswer` is extracted as a direct response to its `name` question. Pair with matching visible HTML.
45
+
46
+ ```json
47
+ {
48
+ "@context": "https://schema.org",
49
+ "@type": "FAQPage",
50
+ "mainEntity": [{
51
+ "@type": "Question",
52
+ "name": "How do I add a canonical tag in Next.js?",
53
+ "acceptedAnswer": {
54
+ "@type": "Answer",
55
+ "text": "In App Router: export const metadata = { alternates: { canonical: 'https://example.com/page' } }. In Pages Router: use <Head><link rel=\"canonical\" href=\"...\" /></Head>."
56
+ }
57
+ }]
58
+ }
59
+ ```
60
+
61
+ Rules: question text = natural-language query; answer = self-contained under 300 chars for voice engines.
62
+
63
+ ### HowTo schema with numbered steps
64
+
65
+ ```json
66
+ {
67
+ "@context": "https://schema.org",
68
+ "@type": "HowTo",
69
+ "name": "How to configure robots.txt for a Next.js site",
70
+ "step": [
71
+ { "@type": "HowToStep", "position": 1, "name": "Create the file", "text": "Add robots.txt to public/." },
72
+ { "@type": "HowToStep", "position": 2, "name": "Add directives", "text": "Set User-agent, Disallow, Allow, and Sitemap URL." }
73
+ ]
74
+ }
75
+ ```
76
+
77
+ Match with a visible `<ol>` — never schema without HTML steps.
78
+
79
+ ### QAPage schema for single-question articles
80
+
81
+ Use `QAPage` for knowledge-base and support docs structured around one primary question. Include `author` + `answerCount` fields.
82
+
83
+ ### Entity-first writing
84
+
85
+ Define the subject before explaining it. AI engines build knowledge-graph connections from entity definitions.
86
+
87
+ ```html
88
+ <!-- Good: entity defined first -->
89
+ <h2>Core Web Vitals</h2>
90
+ <p>Core Web Vitals are three Google-defined UX metrics: LCP, INP, and CLS. They are used as ranking signals in Google Search since 2021.</p>
91
+ ```
92
+
93
+ ---
94
+
95
+ ## 3. Technical Signals
96
+
97
+ ### llms.txt / llms-full.txt
98
+
99
+ The `llms.txt` convention (analogous to `robots.txt` for AI agents) lets agents discover curated content. Serve at domain root.
100
+
101
+ ```
102
+ # /llms.txt
103
+ > ExampleCo builds developer tooling for SEO automation.
104
+
105
+ ## Docs
106
+ - [API Reference](https://example.com/docs/api/llms.txt): Full API docs
107
+ - [Getting Started](https://example.com/docs/start/llms.txt): Setup guide
108
+ ```
109
+
110
+ `llms-full.txt` = concatenated full-text for one-shot context loading.
111
+
112
+ ### robots.txt AI bot directives
113
+
114
+ Known AI crawler `User-agent` strings:
115
+
116
+ | Bot | Owner | Purpose |
117
+ |-----|-------|---------|
118
+ | `GPTBot` | OpenAI | ChatGPT training + search |
119
+ | `OAI-SearchBot` | OpenAI | ChatGPT Search real-time retrieval |
120
+ | `ClaudeBot` | Anthropic | Claude training |
121
+ | `anthropic-ai` | Anthropic | Claude real-time retrieval |
122
+ | `PerplexityBot` | Perplexity | Real-time answer grounding |
123
+ | `Google-Extended` | Google | Gemini training (NOT AI Overviews) |
124
+ | `Googlebot` | Google | Organic + AI Overviews |
125
+
126
+ Blocking `Googlebot` blocks AI Overviews. Block `Google-Extended` to opt out of Gemini training only.
127
+
128
+ ```
129
+ User-agent: GPTBot
130
+ Allow: /docs/
131
+ Disallow: /
132
+
133
+ User-agent: Google-Extended
134
+ Disallow: /
135
+
136
+ Sitemap: https://example.com/sitemap.xml
137
+ ```
138
+
139
+ ### Sitemap with lastmod
140
+
141
+ Include `<lastmod>` reflecting actual content change dates — not today's build date. AI engines prefer recently-updated sources.
142
+
143
+ ### Clean HTML semantics
144
+
145
+ AI parsers rely on heading hierarchy. Rules:
146
+ - One `<h1>` per page — becomes the answer title in AI responses.
147
+ - `<h2>` maps to extractable sub-questions.
148
+ - Never skip heading levels.
149
+ - Use `<article>`, `<section>`, `<main>` over `<div>` wrappers.
150
+
151
+ ---
152
+
153
+ ## 4. Content Quality Signals
154
+
155
+ ### E-E-A-T
156
+
157
+ | Signal | Implementation |
158
+ |--------|---------------|
159
+ | Experience | First-person case studies, original screenshots, tested procedures |
160
+ | Expertise | Author byline + `Person` schema with `sameAs` links (LinkedIn, GitHub) |
161
+ | Authoritativeness | `Organization` schema with `sameAs` to Wikidata/Crunchbase |
162
+ | Trustworthiness | HTTPS, `dateModified` on articles, correction notices |
163
+
164
+ ```json
165
+ {
166
+ "@context": "https://schema.org",
167
+ "@type": "Article",
168
+ "headline": "How to Optimize robots.txt for AI Crawlers",
169
+ "author": {
170
+ "@type": "Person",
171
+ "name": "Jane Doe",
172
+ "sameAs": ["https://github.com/janedoe", "https://linkedin.com/in/janedoe"]
173
+ },
174
+ "datePublished": "2026-03-01",
175
+ "dateModified": "2026-04-10"
176
+ }
177
+ ```
178
+
179
+ ### Citation-worthy content
180
+
181
+ - Statistics with sources: "76% of developers use React (State of JS 2024)" — not "most developers."
182
+ - Dated claims: include year on every statistic.
183
+ - Expert quotes in `<blockquote cite="...">` with named speaker.
184
+ - Outbound links to primary sources (w3.org, ietf.org, schema.org, developers.google.com, developer.mozilla.org, arxiv.org) — AI engines treat authoritative outbound links as a credibility signal.
185
+
186
+ ---
187
+
188
+ ## 5. Grep Patterns for Automated Detection
189
+
190
+ All patterns below yield `INFO` + `heuristic` findings.
191
+
192
+ ### Missing FAQ schema on FAQ-like content
193
+
194
+ ```
195
+ Detect: (?i)(frequently asked questions|faq|common questions) in *.html,*.tsx,*.jsx,*.vue,*.svelte,*.astro
196
+ Then check: "FAQPage" in same file or its layout
197
+ Flag if: FAQ heading found, no FAQPage schema present
198
+ ```
199
+
200
+ ### Missing llms.txt
201
+
202
+ ```
203
+ Glob: llms.txt in public/, static/, site root
204
+ Flag if: not found
205
+ ```
206
+
207
+ ### robots.txt missing AI bot directives
208
+
209
+ ```
210
+ File: robots.txt
211
+ Pattern: GPTBot|ClaudeBot|PerplexityBot|Google-Extended|OAI-SearchBot|anthropic-ai
212
+ Flag if: robots.txt exists but no AI-specific User-agent rules found
213
+ ```
214
+
215
+ ### How-to headings without HowTo schema
216
+
217
+ ```
218
+ Detect: (?i)<h[2-3][^>]*>how (to|do|can|should)\s in *.html,*.tsx,*.jsx,*.vue,*.svelte,*.astro
219
+ Then check: "HowTo" in same file or layout
220
+ Flag if: how-to heading found, no HowTo schema present
221
+ ```
222
+
223
+ ### Missing author/date metadata on articles
224
+
225
+ ```
226
+ Detect: (?i)<article|"@type"\s*:\s*"Article" in *.html,*.tsx,*.jsx,*.vue,*.svelte,*.astro
227
+ Then check: datePublished|rel="author"|<time datetime
228
+ Flag if: article detected, neither datePublished in JSON-LD nor <time datetime> in HTML
229
+ ```
230
+
231
+ ---
232
+
233
+ ## Checklist (Category 6 AEO additions)
234
+
235
+ - [ ] `llms.txt` present at domain root.
236
+ - [ ] `robots.txt` has explicit AI bot directives.
237
+ - [ ] Sitemap `<lastmod>` reflects actual content change dates.
238
+ - [ ] FAQ content uses `FAQPage` schema with self-contained answers.
239
+ - [ ] How-to content uses `HowTo` schema with `step` array + visible `<ol>`.
240
+ - [ ] Single-question pages use `QAPage` schema.
241
+ - [ ] Articles have `datePublished` + `dateModified` in JSON-LD.
242
+ - [ ] Author bylines use `Person` schema with `sameAs` links.
243
+ - [ ] Statistics include inline source citations with year.
244
+ - [ ] `<h2>` headings phrased as natural-language questions where applicable.
245
+ - [ ] Entity terms defined on first use.
246
+
247
+ ---
248
+
249
+ ## References
250
+
251
+ - llmstxt.org spec: https://llmstxt.org
252
+ - OpenAI GPTBot docs: https://platform.openai.com/docs/gptbot
253
+ - Google-Extended opt-out: https://developers.google.com/search/docs/crawling-indexing/overview-google-crawlers
254
+ - Google E-E-A-T guidance: https://developers.google.com/search/docs/fundamentals/creating-helpful-content
255
+ - Schema.org FAQPage: https://schema.org/FAQPage
256
+ - Schema.org HowTo: https://schema.org/HowTo
257
+ - Schema.org QAPage: https://schema.org/QAPage
258
+ - Schema.org Article: https://schema.org/Article
259
+ - Princeton/Georgia Tech GEO paper (Aggarwal et al., 2023): https://arxiv.org/abs/2311.09735