docs: update all documentation and add AI tooling configs
- Rewrite README.md with current architecture, features and stack - Update docs/API.md with all current endpoints (corporate, BI, client 360) - Update docs/ARCHITECTURE.md with cache, modular queries, services, ETL - Update docs/GUIA-USUARIO.md for all roles (admin, corporate, agente) - Add docs/INDEX.md documentation index - Add PROJETO.md comprehensive project reference - Add BI-CCC-Implementation-Guide.md - Include AI agent configs (.claude, .agents, .gemini, _bmad) - Add netbird VPN configuration - Add status report Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,551 @@
|
||||
# SEO Validation Guide
|
||||
|
||||
**For:** WDS Agents performing Agentic Development
|
||||
**Purpose:** Verify SEO implementation against specification before presenting to user
|
||||
**When:** After a public page is built and previewable (browser or deployed)
|
||||
|
||||
---
|
||||
|
||||
## Core Principle
|
||||
|
||||
**Every public page must pass SEO validation before approval.**
|
||||
|
||||
The agent verifies all measurable SEO criteria using browser tools (Puppeteer, MCP browser-tools, or manual inspection). SEO failures caught during development cost minutes to fix. SEO failures caught after deployment cost rankings and traffic.
|
||||
|
||||
---
|
||||
|
||||
## When to Run SEO Validation
|
||||
|
||||
| Trigger | Action |
|
||||
|---------|--------|
|
||||
| Public page section complete (4c/4d) | Run SEO checks before presenting |
|
||||
| Full page implementation complete | Run complete SEO audit |
|
||||
| Pre-deployment review | Full validation against spec + project brief |
|
||||
| Post-deployment verification | Validate live URL matches specification |
|
||||
|
||||
---
|
||||
|
||||
## Reference Documents
|
||||
|
||||
Before running validation, gather:
|
||||
|
||||
1. **Page specification** — SEO & Search section (keywords, URL, headings, meta)
|
||||
2. **Project brief** — SEO Strategy section (page-keyword map, structured data plan)
|
||||
3. **SEO Strategy Guide** — `../../data/agent-guides/saga/seo-strategy-guide.md`
|
||||
|
||||
---
|
||||
|
||||
## SEO Validation Checklist
|
||||
|
||||
### Level 1: Critical (Must Pass)
|
||||
|
||||
These are the top errors found in real-world SEO audits. Failing any of these blocks approval.
|
||||
|
||||
#### 1.1 Page Title Tag
|
||||
|
||||
```
|
||||
Verify:
|
||||
- Title tag exists and is not empty
|
||||
- Length ≤ 60 characters (check each language)
|
||||
- Contains primary keyword
|
||||
- Contains brand name
|
||||
- Is unique (different from other pages)
|
||||
- Matches specification
|
||||
|
||||
Report:
|
||||
"Page title is 'Bilservice på Öland | Källa Fordonservice' (51 chars)
|
||||
— contains keyword 'bilservice', includes brand. ✓ Passes"
|
||||
|
||||
"Page title is 'Home' (4 chars)
|
||||
— too short, no keyword, no brand. ✗ Fails"
|
||||
```
|
||||
|
||||
#### 1.2 Meta Description
|
||||
|
||||
```
|
||||
Verify:
|
||||
- Meta description tag exists and is not empty
|
||||
- Length 150-160 characters
|
||||
- Contains primary keyword
|
||||
- Contains call-to-action
|
||||
- Matches specification
|
||||
|
||||
Report:
|
||||
"Meta description is 156 chars, contains 'bilservice Öland', ends with
|
||||
'Ring oss idag!' ✓ Passes"
|
||||
|
||||
"Meta description is missing. ✗ Fails — 80% of audited sites miss this"
|
||||
```
|
||||
|
||||
#### 1.3 H1 Heading
|
||||
|
||||
```
|
||||
Verify:
|
||||
- Exactly ONE <h1> tag on the page
|
||||
- Contains primary keyword (natural, not stuffed)
|
||||
- Is visible (not hidden)
|
||||
- Matches specification
|
||||
|
||||
Report:
|
||||
"Found 1 <h1>: 'Bilservice och reparationer på Öland'
|
||||
— contains keyword 'bilservice'. ✓ Passes"
|
||||
|
||||
"Found 0 <h1> tags. ✗ Fails — 75% of audited sites have H1 issues"
|
||||
|
||||
"Found 3 <h1> tags. ✗ Fails — only one H1 allowed per page"
|
||||
```
|
||||
|
||||
#### 1.4 Heading Hierarchy
|
||||
|
||||
```
|
||||
Verify:
|
||||
- Headings follow logical order (H1 → H2 → H3)
|
||||
- No skipped levels (H1 → H3 without H2)
|
||||
- No duplicate H1
|
||||
|
||||
Report:
|
||||
"Heading hierarchy: H1 → H2 → H3 → H2 → H3 ✓ Logical flow"
|
||||
|
||||
"Heading hierarchy: H1 → H3 (skipped H2) ✗ Fix: Change H3 to H2"
|
||||
```
|
||||
|
||||
#### 1.5 Image Alt Text
|
||||
|
||||
```
|
||||
Verify:
|
||||
- ALL images have alt attributes
|
||||
- Alt text is descriptive (not empty, not "image")
|
||||
- Alt text exists in all required languages
|
||||
- Decorative images have alt="" (empty, not missing)
|
||||
|
||||
Report:
|
||||
"Found 8 images:
|
||||
hero-image: alt='Källa Fordonservice verkstad...' ✓
|
||||
service-ac: alt='AC-service på personbil' ✓
|
||||
icon-phone: alt='' (decorative) ✓
|
||||
team-photo: alt attribute MISSING ✗
|
||||
|
||||
Result: 7/8 images pass. 1 missing alt text."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Level 2: Important (Should Pass)
|
||||
|
||||
#### 2.1 Open Graph / Social Sharing
|
||||
|
||||
```
|
||||
Verify:
|
||||
- og:title tag present
|
||||
- og:description tag present
|
||||
- og:image tag present (valid URL, image exists)
|
||||
- og:type tag present
|
||||
- twitter:card tag present
|
||||
|
||||
Report:
|
||||
"Social sharing tags:
|
||||
og:title: 'Bilservice Öland — Källa Fordonservice' ✓
|
||||
og:description: present (148 chars) ✓
|
||||
og:image: '/images/social/hem-social.jpg' ✓ (file exists)
|
||||
og:type: 'website' ✓
|
||||
twitter:card: 'summary_large_image' ✓
|
||||
All social tags present."
|
||||
|
||||
"Missing: og:image ✗ — 70% of audited sites miss social tags"
|
||||
```
|
||||
|
||||
#### 2.2 Structured Data (Schema.org)
|
||||
|
||||
```
|
||||
Verify:
|
||||
- JSON-LD script tag exists
|
||||
- Schema type matches project brief plan
|
||||
- Required properties present (name, address, phone for LocalBusiness)
|
||||
- JSON is valid (parseable)
|
||||
|
||||
Report:
|
||||
"Structured data found:
|
||||
@type: 'AutoRepair' ✓
|
||||
name: 'Källa Fordonservice' ✓
|
||||
address: complete ✓
|
||||
telephone: '+46485-27070' ✓
|
||||
openingHours: present ✓
|
||||
JSON-LD validates. ✓ Passes"
|
||||
|
||||
"No structured data found. ✗ Fails — spec requires LocalBusiness schema"
|
||||
```
|
||||
|
||||
#### 2.3 Internal Links
|
||||
|
||||
```
|
||||
Verify:
|
||||
- Page has at least 2 internal links to other pages
|
||||
- Links have descriptive anchor text (not "click here", "read more")
|
||||
- No broken internal links (404s)
|
||||
- No redirect chains (link → 301 → 301 → page)
|
||||
|
||||
Report:
|
||||
"Internal links found: 5
|
||||
'Läs mer om AC-service' → /ac-service ✓ Descriptive
|
||||
'Ring oss' → tel:+46485-27070 ✓ CTA
|
||||
'Klicka här' → /kontakt ✗ Non-descriptive anchor text
|
||||
|
||||
Result: 4/5 links pass."
|
||||
```
|
||||
|
||||
#### 2.4 URL / Slug
|
||||
|
||||
```
|
||||
Verify:
|
||||
- URL slug matches specification
|
||||
- Slug is lowercase
|
||||
- Uses hyphens (not underscores or spaces)
|
||||
- No special characters (ä, ö, å)
|
||||
- Keyword present in slug
|
||||
|
||||
Report:
|
||||
"URL slug: /ac-service ✓ Matches spec, lowercase, keyword present"
|
||||
|
||||
"URL slug: /Sida?id=42 ✗ Not descriptive, no keyword"
|
||||
```
|
||||
|
||||
#### 2.5 Canonical URL
|
||||
|
||||
```
|
||||
Verify:
|
||||
- <link rel="canonical"> tag present
|
||||
- Points to the correct URL (self-referencing)
|
||||
- One canonical per page
|
||||
|
||||
Report:
|
||||
"Canonical: <link rel='canonical' href='https://kallafordon.se/ac-service'> ✓"
|
||||
|
||||
"Canonical tag missing. ✗ Fails"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Level 3: Technical (Verify on Deployment)
|
||||
|
||||
These checks apply to the deployed/preview site, not the prototype.
|
||||
|
||||
#### 3.1 Performance
|
||||
|
||||
```
|
||||
Verify:
|
||||
- Total page weight < 3MB
|
||||
- Largest image < 400KB (hero) / < 200KB (other)
|
||||
- Time to First Byte (TTFB) < 1.5s
|
||||
- No uncompressed images (should be WebP or compressed JPEG)
|
||||
|
||||
Report:
|
||||
"Page weight: 1.8MB ✓ (target < 3MB)
|
||||
hero.jpg: 380KB ✓ (target < 400KB)
|
||||
team.jpg: 1.2MB ✗ (target < 200KB — compress!)
|
||||
icon.svg: 3KB ✓
|
||||
TTFB: 0.8s ✓ (target < 1.5s)"
|
||||
```
|
||||
|
||||
#### 3.2 robots.txt
|
||||
|
||||
```
|
||||
Verify:
|
||||
- robots.txt exists (not 404)
|
||||
- Allows crawling of public pages
|
||||
- References sitemap
|
||||
- Blocks admin/private pages
|
||||
|
||||
Report:
|
||||
"robots.txt: exists ✓
|
||||
Sitemap reference: present ✓
|
||||
Public pages: allowed ✓
|
||||
/wp-admin/: blocked ✓"
|
||||
```
|
||||
|
||||
#### 3.3 XML Sitemap
|
||||
|
||||
```
|
||||
Verify:
|
||||
- Sitemap exists at /sitemap.xml (or referenced location)
|
||||
- Contains all public pages
|
||||
- All URLs return 200 (no broken links)
|
||||
- Includes all language versions (if multilingual)
|
||||
|
||||
Report:
|
||||
"Sitemap: 32 URLs, all return 200 ✓
|
||||
Includes /en/ versions ✓
|
||||
Includes /de/ versions ✓"
|
||||
```
|
||||
|
||||
#### 3.4 hreflang Tags (Multilingual)
|
||||
|
||||
```
|
||||
Verify:
|
||||
- Each page declares all language alternates
|
||||
- x-default points to primary language
|
||||
- Tags are reciprocal (EN page links to SE, SE page links to EN)
|
||||
|
||||
Report:
|
||||
"hreflang tags on /ac-service:
|
||||
sv: /ac-service ✓
|
||||
en: /en/ac-service ✓
|
||||
de: /de/ac-service ✓
|
||||
x-default: /ac-service ✓
|
||||
All reciprocal. ✓ Passes"
|
||||
```
|
||||
|
||||
#### 3.5 Security Headers
|
||||
|
||||
```
|
||||
Verify:
|
||||
- HSTS present
|
||||
- X-Content-Type-Options present
|
||||
- X-Frame-Options present
|
||||
- Referrer-Policy present
|
||||
|
||||
Report:
|
||||
"Security headers: 2/6 present ✗
|
||||
HSTS: missing
|
||||
CSP: missing
|
||||
X-Content-Type-Options: 'nosniff' ✓
|
||||
X-Frame-Options: 'DENY' ✓
|
||||
Referrer-Policy: missing
|
||||
Permissions-Policy: missing
|
||||
|
||||
Note: 95% of audited sites fail security headers."
|
||||
```
|
||||
|
||||
#### 3.6 Favicon
|
||||
|
||||
```
|
||||
Verify:
|
||||
- Favicon exists (check <link rel="icon">)
|
||||
- Multiple sizes available (16x16, 32x32, 180x180)
|
||||
|
||||
Report:
|
||||
"Favicon: present ✓
|
||||
16x16: ✓
|
||||
32x32: ✓
|
||||
apple-touch-icon (180x180): ✓"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verification with Puppeteer
|
||||
|
||||
### Automated SEO Check Script Pattern
|
||||
|
||||
```javascript
|
||||
// Navigate to page
|
||||
await page.goto(pageUrl, { waitUntil: 'networkidle0' });
|
||||
|
||||
// 1. Title tag
|
||||
const title = await page.title();
|
||||
console.log(`Title: "${title}" (${title.length} chars)`);
|
||||
|
||||
// 2. Meta description
|
||||
const metaDesc = await page.$eval(
|
||||
'meta[name="description"]',
|
||||
el => el.content
|
||||
).catch(() => null);
|
||||
console.log(`Meta desc: "${metaDesc}" (${metaDesc?.length || 0} chars)`);
|
||||
|
||||
// 3. H1 count and content
|
||||
const h1s = await page.$$eval('h1', els => els.map(el => el.textContent.trim()));
|
||||
console.log(`H1 tags: ${h1s.length} — "${h1s.join('", "')}"`);
|
||||
|
||||
// 4. Heading hierarchy
|
||||
const headings = await page.$$eval('h1,h2,h3,h4,h5,h6', els =>
|
||||
els.map(el => ({ tag: el.tagName, text: el.textContent.trim().substring(0, 50) }))
|
||||
);
|
||||
console.log('Heading hierarchy:', headings.map(h => h.tag).join(' → '));
|
||||
|
||||
// 5. Images without alt
|
||||
const imagesNoAlt = await page.$$eval('img', els =>
|
||||
els.filter(el => !el.hasAttribute('alt')).map(el => el.src)
|
||||
);
|
||||
console.log(`Images without alt: ${imagesNoAlt.length}`);
|
||||
|
||||
// 6. Open Graph tags
|
||||
const ogTags = await page.$$eval('meta[property^="og:"]', els =>
|
||||
els.map(el => ({ property: el.getAttribute('property'), content: el.content }))
|
||||
);
|
||||
console.log(`OG tags: ${ogTags.length}`, ogTags);
|
||||
|
||||
// 7. Structured data
|
||||
const jsonLd = await page.$$eval('script[type="application/ld+json"]', els =>
|
||||
els.map(el => JSON.parse(el.textContent))
|
||||
).catch(() => []);
|
||||
console.log(`Structured data: ${jsonLd.length} blocks`, jsonLd.map(j => j['@type']));
|
||||
|
||||
// 8. Canonical
|
||||
const canonical = await page.$eval('link[rel="canonical"]', el => el.href).catch(() => null);
|
||||
console.log(`Canonical: ${canonical || 'MISSING'}`);
|
||||
|
||||
// 9. Internal links
|
||||
const links = await page.$$eval('a[href]', els =>
|
||||
els.filter(el => el.href.startsWith(window.location.origin))
|
||||
.map(el => ({ text: el.textContent.trim().substring(0, 40), href: el.href }))
|
||||
);
|
||||
console.log(`Internal links: ${links.length}`);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Narration Pattern
|
||||
|
||||
Group results by severity and narrate clearly:
|
||||
|
||||
```
|
||||
## SEO Validation Report: {Page Name}
|
||||
|
||||
### Critical ✓/✗
|
||||
Title tag: "Bilservice Öland | Källa Fordonservice" (51 chars) ✓
|
||||
Meta description: "Komplett bilverkstad..." (156 chars) ✓
|
||||
H1: 1 found — "Bilservice och reparationer på Öland" ✓
|
||||
Heading hierarchy: H1 → H2 → H3 → H2 → H3 ✓
|
||||
Image alt text: 7/8 images have alt ✗ (team-photo missing)
|
||||
|
||||
### Important ✓/✗
|
||||
Open Graph: 5/5 tags present ✓
|
||||
Structured data: AutoRepair schema valid ✓
|
||||
Internal links: 5 found, 4/5 descriptive ✗ (1 "Klicka här")
|
||||
URL slug: /ac-service ✓
|
||||
Canonical: present, self-referencing ✓
|
||||
|
||||
### Technical (deployment only)
|
||||
Page weight: 1.8MB ✓
|
||||
Image sizes: 1 oversized (team.jpg 1.2MB) ✗
|
||||
Security headers: 2/6 ✗
|
||||
|
||||
### Summary
|
||||
Critical: 4/5 pass
|
||||
Important: 4/5 pass
|
||||
Technical: 1/3 pass
|
||||
|
||||
Action needed: Fix 1 missing alt text, 1 non-descriptive link,
|
||||
1 oversized image, 4 security headers.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration with Phase 5 Flow
|
||||
|
||||
```
|
||||
4a: Announce & Gather
|
||||
4b: Create Story File
|
||||
4c: Implement Section
|
||||
↓
|
||||
Agent runs Puppeteer verification (INLINE-TESTING-GUIDE)
|
||||
Agent runs SEO validation (THIS GUIDE) — for public pages only
|
||||
↓
|
||||
All pass? ── No ──→ Agent fixes, re-verifies (loop)
|
||||
│
|
||||
Yes
|
||||
↓
|
||||
4d: Present for Testing
|
||||
```
|
||||
|
||||
### Story File Addition
|
||||
|
||||
Add SEO criteria to the story file's Agent-Verifiable section:
|
||||
|
||||
```markdown
|
||||
### SEO Criteria (Public Pages)
|
||||
|
||||
| # | Criterion | Expected | How to Verify |
|
||||
|---|-----------|----------|---------------|
|
||||
| S1 | Title tag | "Bilservice Öland \| Källa" ≤60 chars | Read document.title |
|
||||
| S2 | Meta description | 150-160 chars, keyword present | Read meta[name=description] |
|
||||
| S3 | H1 count | Exactly 1 | Count h1 elements |
|
||||
| S4 | H1 keyword | Contains "bilservice" | Read h1 textContent |
|
||||
| S5 | Heading hierarchy | H1→H2→H3, no skips | Scan all headings |
|
||||
| S6 | Image alt coverage | 100% images have alt | Check img elements |
|
||||
| S7 | OG tags | og:title, og:description, og:image | Check meta[property^=og:] |
|
||||
| S8 | Internal links | ≥ 2, descriptive text | Count and check a[href] |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration with Acceptance Testing
|
||||
|
||||
When creating test scenarios (Phase 4 [H] Handover / Phase 5 [T] Acceptance Testing), include SEO as a test category:
|
||||
|
||||
```yaml
|
||||
seo_checks:
|
||||
- id: 'SEO-001'
|
||||
name: 'Page title correct'
|
||||
verify:
|
||||
- 'Title tag matches specification'
|
||||
- 'Title ≤ 60 characters'
|
||||
- 'Contains primary keyword'
|
||||
|
||||
- id: 'SEO-002'
|
||||
name: 'Meta description correct'
|
||||
verify:
|
||||
- 'Meta description matches specification'
|
||||
- 'Length 150-160 characters'
|
||||
- 'Contains CTA'
|
||||
|
||||
- id: 'SEO-003'
|
||||
name: 'Heading structure valid'
|
||||
verify:
|
||||
- 'Exactly one H1'
|
||||
- 'No skipped heading levels'
|
||||
|
||||
- id: 'SEO-004'
|
||||
name: 'Image alt text complete'
|
||||
verify:
|
||||
- 'All content images have alt text'
|
||||
- 'Alt text in correct language'
|
||||
|
||||
- id: 'SEO-005'
|
||||
name: 'Structured data valid'
|
||||
verify:
|
||||
- 'JSON-LD present and parseable'
|
||||
- 'Schema type matches plan'
|
||||
- 'Required properties present'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
- **Never skip SEO validation on public pages** — It's not optional
|
||||
- **Never approve a page with missing alt text** — 85% of real sites fail this
|
||||
- **Never use "click here" or "read more" as link text** — Describe the destination
|
||||
- **Never have more than one H1** — One per page, always
|
||||
- **Never deploy without meta description** — 80% of sites miss this
|
||||
- **Never assume SEO "can be added later"** — It's specification, not decoration
|
||||
|
||||
---
|
||||
|
||||
## Common Fixes (From 44 Real-World Audits)
|
||||
|
||||
| Issue | Frequency | Fix Time | Fix |
|
||||
|-------|-----------|----------|-----|
|
||||
| Missing alt text | 85% | 1 min/image | Add descriptive alt attribute |
|
||||
| Missing meta description | 80% | 2 min/page | Add meta tag from spec |
|
||||
| H1 missing or wrong | 75% | 1 min | Add/fix h1 tag |
|
||||
| Missing OG tags | 70% | 3 min/page | Add og: meta tags from spec |
|
||||
| Missing structured data | 65% | 5 min/page | Add JSON-LD script |
|
||||
| Oversized images | 65% | 2 min/image | Compress + convert to WebP |
|
||||
| Non-descriptive links | 30% | 1 min/link | Rewrite anchor text |
|
||||
| Missing canonical | 40% | 1 min | Add link rel=canonical |
|
||||
|
||||
**Total estimated fix time for a typical page: 15-20 minutes**
|
||||
These are all preventable by validating during development.
|
||||
|
||||
---
|
||||
|
||||
## Related Resources
|
||||
|
||||
- **Inline Testing Guide:** `INLINE-TESTING-GUIDE.md` — General Puppeteer verification
|
||||
- **SEO Strategy Guide:** `../../data/agent-guides/saga/seo-strategy-guide.md` — SEO reference
|
||||
- **SEO Content Instructions:** `../../4-ux-design/templates/instructions/seo-content.instructions.md` — Spec-level SEO
|
||||
- **Specification Quality:** `../../data/agent-guides/freya/specification-quality.md` — Quality checklist
|
||||
- **Meta Content Guide:** `../../data/agent-guides/freya/meta-content-guide.md` — Meta tag details
|
||||
|
||||
---
|
||||
|
||||
*SEO validation during development = zero SEO issues at launch. Validate as you build.*
|
||||
Reference in New Issue
Block a user