
Seo Drift
- 22 installs
- 548 repo stars
- Updated July 20, 2026
- agricidaniel/codex-seo
This is a copy of seo-drift by agricidaniel - installs and ranking accrue to the original listing.
seo-drift is a Claude Code skill that captures baselines of on-page SEO elements and detects regressions over time, like git for SEO.
About
seo-drift monitors SEO drift by capturing baselines of on-page SEO-critical elements and diffing them over time. It records titles, meta descriptions, canonicals, robots directives, headings, JSON-LD schema, Open Graph tags, and Core Web Vitals into a local SQLite store, then applies 17 comparison rules to flag regressions. A developer uses it as a deployment check to catch SEO-breaking changes before or after shipping.
- Git for SEO: capture baselines and diff on-page SEO changes over time
- Records titles, meta, canonicals, headings, schema, OG tags, and Core Web Vitals
- Applies 17 comparison rules across 3 severity levels to flag regressions
Seo Drift by the numbers
- 22 all-time installs (skills.sh)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
seo-drift capabilities & compatibility
Free; stores baselines locally in SQLite with no required API keys.
- Capabilities
- seo audit · seo content · seo cluster
- Use cases
- seo · marketing · testing
- Runs
- Runs locally
- Pricing
- Free
What seo-drift says it does
SEO drift monitoring: capture baselines of SEO-critical elements, detect changes, and track regressions over time.
The comparison engine applies **17 rules across 3 severity levels**.
npx skills add https://github.com/agricidaniel/codex-seo --skill seo-driftAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 22 |
|---|---|
| repo stars | ★ 548 |
| Last updated | July 20, 2026 |
| Repository | agricidaniel/codex-seo ↗ |
What it does
Capture SEO baselines and diff on-page elements over time to catch regressions after deployments.
Who is it for?
Catching SEO-breaking changes across deployments by diffing against a known-good baseline.
Skip if: Keyword research or backlink analysis.
When should I use this skill?
You want to baseline a page or check whether a deploy changed SEO-critical elements.
What you get
Changes to SEO-critical elements are detected and graded by severity against a baseline.
- SEO baseline snapshots
- Change diff with severity
- Drift history over time
By the numbers
- 17 comparison rules across 3 severity levels
- 13 SEO-critical elements captured per baseline
Files
SEO Drift Monitor (April 2026)
Shared Data Cache
Step 0 -- Check shared data cache:
Before gathering, check .seo-cache/ for reusable context from related SEO skills. Reference: ../seo/references/shared-data-cache.md for schemas and dependency map.
Check these cache files when present:
.seo-cache/site-meta.jsonfor domain, business type, industry, and crawl context.seo-cache/audit-scores.jsonfor prior full-audit priorities.seo-cache/pages/{url-slug}/page-analysis.jsonfor page-level context when a URL is provided
- If found: parse and use clearly valid fields (note "Using cached [X] from [date]")
- If missing, corrupt, or irrelevant: continue with fresh evidence
- If the user says "refresh" or "re-run": ignore cache reads and overwrite on write
Git for your SEO. Capture baselines, detect regressions, track changes over time.
---
Commands
| Command | Purpose |
|---|---|
/seo drift baseline <url> | Capture current SEO state as a "known good" snapshot |
/seo drift compare <url> | Compare current page state to stored baseline |
/seo drift history <url> | Show change history and past comparisons |
---
What It Captures
Every baseline records these SEO-critical elements:
| Element | Field | Source |
|---|---|---|
| Title tag | title | parse_html.py |
| Meta description | meta_description | parse_html.py |
| Canonical URL | canonical | parse_html.py |
| Robots directives | meta_robots | parse_html.py |
| H1 headings | h1 (array) | parse_html.py |
| H2 headings | h2 (array) | parse_html.py |
| H3 headings | h3 (array) | parse_html.py |
| JSON-LD schema | schema (array) | parse_html.py |
| Open Graph tags | open_graph (dict) | parse_html.py |
| Core Web Vitals | cwv (dict) | pagespeed_check.py |
| HTTP status code | status_code | fetch_page.py |
| HTML content hash | html_hash (SHA-256) | Computed |
| Schema content hash | schema_hash (SHA-256) | Computed |
---
How Comparison Works
The comparison engine applies 17 rules across 3 severity levels. Load references/comparison-rules.md for the full rule set with thresholds, recommended actions, and cross-skill references.
Severity Levels
| Level | Meaning | Response Time |
|---|---|---|
| CRITICAL | SEO-breaking change, likely traffic loss | Immediate |
| WARNING | Potential impact, needs investigation | Within 1 week |
| INFO | Awareness only, may be intentional | Review at convenience |
---
Storage
All data is stored locally in SQLite:
~/.cache/codex-seo/drift/baselines.dbTables
- baselines: Captured snapshots with all SEO elements
- comparisons: Diff results with triggered rules and severities
URL normalization ensures consistent matching: lowercase scheme/host, strip default ports (80/443), sort query parameters, remove UTM parameters, strip trailing slashes.
---
Command: baseline
Captures the current state of a page and stores it.
Steps: 1. Validate URL (SSRF protection via google_auth.validate_url()) 2. Fetch page via scripts/fetch_page.py 3. Parse HTML via scripts/parse_html.py 4. Optionally fetch CWV via scripts/pagespeed_check.py (use --skip-cwv to skip) 5. Hash HTML body and schema content (SHA-256) 6. Store snapshot in SQLite
Execution:
python scripts/drift_baseline.py <url>
python scripts/drift_baseline.py <url> --skip-cwvOutput: JSON with baseline ID, timestamp, URL, and summary of captured elements.
---
Command: compare
Fetches the current page state and diffs it against the most recent baseline.
Steps: 1. Validate URL 2. Load most recent baseline from SQLite (or specific --baseline-id) 3. Fetch and parse current page state 4. Run all 17 comparison rules 5. Classify findings by severity 6. Store comparison result 7. Output JSON diff report
Execution:
python scripts/drift_compare.py <url>
python scripts/drift_compare.py <url> --baseline-id 5
python scripts/drift_compare.py <url> --skip-cwvOutput: JSON with all triggered rules, old/new values, severity, and actions.
After comparison, offer to generate an HTML report:
python scripts/drift_report.py <comparison_json_file> --output drift-report.html---
Command: history
Shows all baselines and comparisons for a URL.
Execution:
python scripts/drift_history.py <url>
python scripts/drift_history.py <url> --limit 10Output: JSON array of baselines (newest first) with timestamps and comparison summaries.
---
Cross-Skill Integration
When drift is detected, recommend the appropriate specialized skill:
| Finding | Recommendation |
|---|---|
| Schema removed or modified | Run /seo schema <url> for full validation |
| CWV regression | Run /seo technical <url> for performance audit |
| Title or meta description changed | Run /seo page <url> for content analysis |
| Canonical changed or removed | Run /seo technical <url> for indexability check |
| Noindex added | Run /seo technical <url> for crawlability audit |
| H1/heading structure changed | Run /seo content <url> for E-E-A-T review |
| OG tags removed | Run /seo page <url> for social sharing analysis |
| Status code changed to error | Run /seo technical <url> for full diagnostics |
---
Error Handling
| Scenario | Action |
|---|---|
| URL unreachable | Report error from fetch_page.py. Do not guess state. Suggest user verify URL. |
| No baseline exists for URL | Inform user and suggest running baseline first. |
| SSRF blocked (private IP) | Report validate_url() rejection. Never bypass. |
| SQLite database missing | Auto-create on first use. No error. |
| CWV fetch fails (no API key) | Store null for CWV fields. Skip CWV rules during comparison. |
| Page returns 4xx/5xx | Still capture as baseline (status code IS a tracked field). |
| Multiple baselines exist | Use most recent unless --baseline-id specified. |
---
Security
- All URL fetching goes through
scripts/fetch_page.pywhich enforces SSRF protection
(blocks private IPs, loopback, reserved ranges, GCP metadata endpoints)
- No curl, no subprocess HTTP calls -- only the project's validated fetch pipeline
- All SQLite queries use parameterized placeholders (
?), never string interpolation - TLS always verified -- no
verify=Falseanywhere in the pipeline
---
Typical Workflows
Pre/Post Deployment Check
/seo drift baseline https://example.com # Before deploy
# ... deploy happens ...
/seo drift compare https://example.com # After deployOngoing Monitoring
/seo drift baseline https://example.com # Initial capture
# ... weeks later ...
/seo drift compare https://example.com # Check for drift
/seo drift history https://example.com # Review all changesInvestigating a Traffic Drop
/seo drift compare https://example.com # What changed?
/seo drift history https://example.com # When did it change?Write to shared data cache
After completing all work, write a concise JSON summary to .seo-cache/ when the workflow produced durable findings. Use the schemas and naming rules in ../seo/references/shared-data-cache.md; include at least cache_type, analyzed_at, source URL/domain, key findings, issues, recommendations, and tool limitations. Add .seo-cache/ to .gitignore if it is missing.
SEO Drift Comparison Rules
17 rules across 3 severity levels. Each rule compares a specific SEO element between the stored baseline and the current page state.
---
CRITICAL (Immediate Action Required)
These changes typically cause measurable traffic loss within days.
Rule 1: Schema/JSON-LD Completely Removed
- Compare: Baseline
schemaarray has items, current is empty - Threshold: Any schema present before, none now
- Action: Restore structured data immediately. Rich results will be lost within hours.
- Cross-ref:
/seo schema <url>
Rule 2: Canonical URL Changed
- Compare: Baseline
canonicalvs currentcanonical - Threshold: Different non-null values (after normalization)
- Action: Verify the new canonical is intentional. Incorrect canonicals redirect ranking signals to wrong page.
- Cross-ref:
/seo technical <url>
Rule 3: Canonical URL Removed
- Compare: Baseline
canonicalwas set, current isnull - Threshold: Had value, now missing
- Action: Restore canonical tag. Google will guess, often incorrectly for pages with query parameters.
- Cross-ref:
/seo technical <url>
Rule 4: Noindex Directive Added
- Compare: Baseline
meta_robotsdid not contain "noindex", current does - Threshold: "noindex" substring now present (case-insensitive)
- Action: If unintentional, remove immediately. Page will be dropped from index within days.
- Cross-ref:
/seo technical <url>
Rule 5: H1 Tag Removed Entirely
- Compare: Baseline
h1had entries, current is empty - Threshold: One or more H1s before, zero now
- Action: Restore H1 heading. Primary page topic signal for search engines.
- Cross-ref:
/seo content <url>
Rule 6: H1 Text Changed Significantly
- Compare: First H1 in baseline vs first H1 in current, SequenceMatcher ratio
- Threshold: Similarity ratio < 0.5 (>50% different)
- Action: Verify the H1 change aligns with target keyword strategy.
- Cross-ref:
/seo content <url>
Rule 7: Title Tag Removed Entirely
- Compare: Baseline
titlewas set, current isnullor empty - Threshold: Had value, now missing
- Action: Restore title tag immediately. Google will auto-generate one, often poorly.
- Cross-ref:
/seo page <url>
Rule 8: HTTP Status Code Changed to Error
- Compare: Baseline
status_codewas 2xx, current is 4xx or 5xx - Threshold: Status code class changed from success to client/server error
- Action: Investigate server error or missing page. Rankings will drop within days.
- Cross-ref:
/seo technical <url>
---
WARNING (Investigate Within 1 Week)
These changes may impact rankings or CTR but are sometimes intentional.
Rule 9: Title Text Changed
- Compare: Baseline
titlevs currenttitle(trimmed) - Threshold: Strings differ (case-sensitive, whitespace-normalized)
- Action: Verify new title includes target keywords. Monitor CTR in GSC over 2 weeks.
- Cross-ref:
/seo page <url>
Rule 10: Meta Description Changed
- Compare: Baseline
meta_descriptionvs currentmeta_description - Threshold: Strings differ (trimmed)
- Action: Verify new description includes call-to-action and target keywords. Monitor CTR.
- Cross-ref:
/seo page <url>
Rule 11: Core Web Vitals Metric Regressed >20%
- Compare: Each CWV metric p75 value (LCP, INP, CLS) baseline vs current
- Threshold: Current value is >20% worse than baseline (higher for LCP/INP, higher for CLS)
- Action: Investigate performance regression. Check recent code changes or third-party scripts.
- Cross-ref:
/seo technical <url>
Rule 12: CWV Performance Score Dropped 10+ Points
- Compare: Lighthouse performance score baseline vs current
- Threshold: Drop of 10 or more points (e.g., 85 to 74)
- Action: Run full PageSpeed analysis to identify new bottlenecks.
- Cross-ref:
/seo google psi <url>
Rule 13: OG Tags Removed
- Compare: Baseline
open_graphhad entries, current is empty - Threshold: One or more OG tags before, none now
- Action: Restore OG tags. Social sharing will show generic/missing previews.
- Cross-ref:
/seo page <url>
Rule 14: Schema/JSON-LD Content Modified
- Compare: Baseline
schema_hashvs currentschema_hash - Threshold: Hash differs AND schema still exists (removal is Rule 1)
- Action: Validate modified schema. Check for type changes, removed properties, or new validation errors.
- Cross-ref:
/seo schema <url>
---
INFO (Awareness Only)
These are tracked for completeness. Often positive or neutral changes.
Rule 15: New Schema/JSON-LD Added
- Compare: Baseline
schemawas empty, current has items - Threshold: No schema before, schema now present
- Action: Positive change. Validate the new schema with
/seo schema <url>. - Cross-ref:
/seo schema <url>
Rule 16: H2 Structure Changed
- Compare: Baseline
h2array vs currenth2array - Threshold: Different number of H2s, or different H2 text values
- Action: Review heading hierarchy. Ensure content sections still align with target topics.
- Cross-ref:
/seo content <url>
Rule 17: Content Hash Changed
- Compare: Baseline
html_hashvs currenthtml_hash - Threshold: Hash differs (catch-all for any body content change)
- Action: General content change detected. Review if no other rules triggered to understand what changed.
- Cross-ref:
/seo page <url>
Related skills
FAQ
What does it capture?
Titles, meta descriptions, canonicals, robots, H1-H3, JSON-LD schema, Open Graph tags, Core Web Vitals, status codes, and content hashes.
How are changes graded?
A comparison engine applies 17 rules across CRITICAL, WARNING, and INFO severity levels.
Where is data stored?
Locally in SQLite at ~/.cache/codex-seo/drift/baselines.db.