| name | daily-seo-brief |
| description | Daily SEO briefing. Analyses content traffic, engagement, and search performance across published guides using internal analytics and Google Search Console. Generates actionable reports with S-### hypotheses. Designed for daily /loop. Load identity first. |
| metadata | {"version":"3.1.0","git_hash":"c05387b"} |
Daily SEO Brief
Implements: commons:daily-brief-pattern — 8-step orchestration sequence (adapted for SEO: performance review + hypothesis scoring + action generation). Also writes to Section 1 of seo-strategy-master per commons:strategy-master-pattern.
Renamed from seo-monitor to daily-seo-brief for naming consistency across all domains. All domain daily briefs follow the daily-{domain}-brief convention.
Daily performance review of all published content. Pulls internal analytics and Google Search Console data, cross-references with the content inventory, and generates an actionable report. Designed for /loop 1d.
Dependencies
Load identity before this skill. Identity defines positioning, ICP, and keyword strategy context. This skill handles performance measurement and recommendations.
Source of Truth
Read the SEO Strategy Master before running this skill:
/var/www/html/systemprompt-web/reports/seo/seo-strategy-master.md
This living document is the single source of truth for:
- Current performance snapshot (updated by this skill on each run)
- 30/60/90 objectives for organic sessions, GSC clicks, CTR, indexing
- Active SEO hypotheses (rendered from the hypothesis ledger)
- Pillar health assessments
- Technical SEO issues
- Content pipeline and gap analysis
Hypothesis ledger:
/var/www/html/systemprompt-web/reports/seo/data/hypothesis-ledger.md
The SEO hypothesis ledger tracks testable SEO actions with S-### IDs. On each run, this skill:
- Checks for hypotheses with
window_end <= today and scores them
- Logs new hypotheses discovered during analysis
- Updates the strategy master's section 3 (Active Hypotheses) rendering
Keyword targets (machine-readable, read by content skills):
/var/www/html/systemprompt-web/reports/seo/data/keyword-targets.json
On each daily run, cross-reference this file with GSC query data to produce the Keyword Movements section. No DataForSEO API calls needed daily. See seo-keyword-tracker skill for the monthly full refresh that updates this file.
Satellite documents (all in reports/seo/):
keyword-research.md -- DataForSEO keyword data (updated monthly by seo-keyword-tracker)
data/keyword-targets.json -- Canonical keyword registry (updated monthly)
data/keyword-snapshots.jsonl -- Append-only keyword history (updated monthly)
guide-inventory.md -- All guides with metadata
metadata-audit.md -- Title and description audits
interlinking-strategy.md -- Internal link map
backlink-strategy.md -- External link opportunities
traffic-analytics.md -- Traffic source history
Daily metrics are appended to reports/seo/data/seo-metrics.jsonl after each run.
Overview
This skill runs eight steps:
- Read the SEO strategy master and inventory all published guides
- Pull internal analytics (systemprompt CLI)
- Pull Google Search Console data (if configured)
- Cross-reference keyword targets with GSC queries (daily keyword intelligence, zero API cost)
- Score maturing hypotheses from the SEO hypothesis ledger
- Analyse performance against strategy targets
- Generate and save a structured report (including Keyword Movements section)
- Append daily metrics to seo-metrics.jsonl and update the strategy master
Step 1: Read Strategy and Inventory Content
First, read the SEO Strategy Master at /var/www/html/systemprompt-web/reports/seo/seo-strategy-master.md. Extract:
- The complete guide inventory tables (slugs, primary keywords, long-tail keywords, search intent, status)
- Keyword cluster map (pillar pages, sub-topics, cluster strength assessments)
- Content gap analysis (high-priority topics not yet covered)
- Suggested future publishing schedule (what should have been published by now)
- Performance benchmarks (100+ sessions/month within 90 days, page 1 ranking within 90 days)
- Internal linking recommendations (what links should exist)
Then read the frontmatter from every guide in /var/www/html/systemprompt-web/services/content/guides/*/index.md to get current metadata:
title, slug, keywords, category, tags
published_at, updated_at
public (true/false)
description (used as meta description)
Cross-reference the live inventory against the strategy document to identify:
- Guides that exist in the strategy but have not been published yet
- Guides that are past their target publication date
- Keyword targets from the strategy that are not reflected in guide frontmatter
Step 2: Pull Internal Analytics
Run these commands to collect traffic and engagement data. Use --json for parseable output and --since 7d for the current period.
systemprompt analytics overview --since 7d --json
systemprompt analytics content top --limit 30 --since 7d --json
systemprompt analytics content trends --since 7d --json
systemprompt analytics content popular --limit 30 --since 7d --json
systemprompt analytics traffic sources --since 7d --json
systemprompt analytics sessions stats --since 7d --json
systemprompt analytics sessions trends --since 7d --json
For week-over-week comparison, also run:
systemprompt analytics content top --limit 30 --since 14d --json
systemprompt analytics sessions stats --since 14d --json
Subtract the 7d data from the 14d data to derive the previous week's numbers.
Key Metrics to Extract
| Metric | Source Command |
|---|
| Sessions per guide | analytics content top |
| Engagement pattern (bounce/skimmer/reader/engaged) | analytics content top |
| Traffic sources (organic/direct/referral/social) | analytics traffic sources |
| Total sessions and visitors | analytics sessions stats |
| Session trends | analytics sessions trends |
| Content popularity ranking | analytics content popular |
Step 3: Pull Google Search Console Data
Prerequisites
GSC integration requires a service account. If not configured, skip this step and note "GSC data: Not configured" in the report.
One-time setup:
- Create a Google Cloud project with the Search Console API enabled
- Create a service account and download the JSON key
- Add the service account email as a user in Google Search Console for
sc-domain:systemprompt.io
- Save the JSON key as
gsc.json in the systemprompt-web .keys/ dir (/var/www/html/systemprompt-web/.keys/gsc.json)
Service Account Key
The key file is at:
/var/www/html/systemprompt-web/.keys/gsc.json
If this file does not exist, skip the GSC step and note "GSC data: Not configured" in the report.
Authentication
Generate a JWT and exchange it for an access token. The service account JSON contains client_email, private_key, and token_uri.
GSC_KEY_FILE="/var/www/html/systemprompt-web/.keys/gsc.json"
ACCESS_TOKEN=$(python3 -c "
import json, sys, time, base64
from urllib.request import urlopen, Request
from urllib.parse import urlencode
from pathlib import Path
# Load key from file
key_file = Path('$GSC_KEY_FILE')
if not key_file.exists():
print('GSC_NOT_CONFIGURED', file=sys.stderr)
sys.exit(1)
try:
key_data = json.loads(key_file.read_text())
except json.JSONDecodeError as e:
print(f'ERROR: gsc.json is not valid JSON: {e}', file=sys.stderr)
sys.exit(1)
# Validate required fields
for field in ('client_email', 'private_key', 'token_uri'):
if field not in key_data:
print(f'ERROR: gsc.json missing required field: {field}', file=sys.stderr)
sys.exit(1)
email = key_data['client_email']
private_key = key_data['private_key']
# Build and sign JWT
now = int(time.time())
claims = {
'iss': email,
'scope': 'https://www.googleapis.com/auth/webmasters.readonly',
'aud': 'https://oauth2.googleapis.com/token',
'iat': now,
'exp': now + 3600
}
try:
import jwt as pyjwt
token = pyjwt.encode(claims, private_key, algorithm='RS256')
except ImportError:
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
header = base64.urlsafe_b64encode(json.dumps({'alg':'RS256','typ':'JWT'}).encode()).rstrip(b'=').decode()
payload = base64.urlsafe_b64encode(json.dumps(claims).encode()).rstrip(b'=').decode()
key = serialization.load_pem_private_key(private_key.encode(), password=None)
message = f'{header}.{payload}'.encode()
signature = key.sign(message, padding.PKCS1v15(), hashes.SHA256())
token = f'{header}.{payload}.{base64.urlsafe_b64encode(signature).rstrip(b\"=\").decode()}'
# Exchange JWT for access token
data = urlencode({
'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'assertion': token
}).encode()
req = Request('https://oauth2.googleapis.com/token', data=data, method='POST')
try:
resp = json.loads(urlopen(req).read())
except Exception as e:
print(f'ERROR: Token exchange failed: {e}', file=sys.stderr)
sys.exit(1)
if 'access_token' not in resp:
print(f'ERROR: No access_token in response. Check service account permissions. Response: {resp}', file=sys.stderr)
sys.exit(1)
print(resp['access_token'])
")
if [ $? -ne 0 ]; then
echo "GSC authentication failed. Skipping Search Console data."
GSC_AVAILABLE=false
else
GSC_AVAILABLE=true
fi
Querying Search Analytics
Only run these queries if GSC_AVAILABLE=true. For each API call, check the HTTP response for errors. A 403 means the service account does not have permission in Search Console. A 401 means the access token is invalid or expired. Any error response should be logged and the GSC section skipped gracefully.
curl -s -X POST \
"https://www.googleapis.com/webmasters/v3/sites/sc-domain%3Asystemprompt.io/searchAnalytics/query" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"startDate": "'$(date -d '7 days ago' +%Y-%m-%d)'",
"endDate": "'$(date -d 'yesterday' +%Y-%m-%d)'",
"dimensions": ["page"],
"rowLimit": 100
}'
curl -s -X POST \
"https://www.googleapis.com/webmasters/v3/sites/sc-domain%3Asystemprompt.io/searchAnalytics/query" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"startDate": "'$(date -d '7 days ago' +%Y-%m-%d)'",
"endDate": "'$(date -d 'yesterday' +%Y-%m-%d)'",
"dimensions": ["query", "page"],
"rowLimit": 500
}'
curl -s -X POST \
"https://www.googleapis.com/webmasters/v3/sites/sc-domain%3Asystemprompt.io/searchAnalytics/query" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"startDate": "'$(date -d '14 days ago' +%Y-%m-%d)'",
"endDate": "'$(date -d '8 days ago' +%Y-%m-%d)'",
"dimensions": ["page"],
"rowLimit": 100
}'
GSC Data to Extract
| Metric | Use |
|---|
| Impressions per page | How visible is each guide in search results |
| Clicks per page | How many searchers actually visit |
| CTR per page | Title/description effectiveness |
| Average position per page | Ranking strength |
| Top queries per page | What searches drive traffic |
| High-impression low-CTR pages | Quick wins for title/meta improvements |
| Position 5-20 queries | Near page-1 ranking opportunities |
Step 4: Cross-Reference Keyword Targets with GSC Queries
Read reports/seo/data/keyword-targets.json. For each tracked keyword:
- Match against GSC query data (fuzzy match: the GSC query contains the tracked keyword string)
- Record position, impressions, clicks, CTR for each matched keyword
- Compare to target_position from keyword-targets.json
- Flag movements: if position changed >2 spots WoW, note as movement
Generate three tables for the report:
Tracked Keywords in GSC:
Keywords from keyword-targets.json that appear in today's GSC data, with their actual vs target position.
Tracked Keywords Missing from GSC:
Keywords we target but that have zero GSC impressions (guide not indexed, or not ranking for this keyword).
Untracked Queries with Impressions:
GSC queries with >30 impressions that don't match any tracked keyword. These are keyword discovery opportunities. For each, recommend: "Add '{query}' to keyword-targets.json" with an action to either assign an existing guide or flag as a content gap.
Daily keyword actions (1-3 per day):
- Tracked keyword dropped >2 positions: "Investigate ranking drop for '{keyword}' on {guide}"
- Tracked keyword with >500 impressions and <1% CTR: "Rewrite title/meta for {guide} to better match '{keyword}'"
- Untracked query with >50 impressions: "Add '{query}' to keyword tracking and assign to {guide_or_gap}"
status: "gap" keyword with volume >200 and difficulty <30: "Create content for '{keyword}' ({volume}/mo)"
Step 5: Analyse Performance
Cross-reference all four data sources (inventory, internal analytics, GSC, keyword targets) to build a complete picture.
Analysis Framework
Winners (top 5 by combined score):
- High sessions AND good engagement (reader or engaged pattern)
- Strong organic traffic OR improving trend
- If GSC available: high clicks, good CTR, improving position
- Analyse WHY: topic relevance, keyword targeting, freshness, internal links
Underperformers (bottom 5 by combined score):
- Low sessions OR declining trend
- Poor engagement (bounce or skimmer pattern)
- If GSC available: low CTR despite impressions, declining position
- Diagnose the specific problem and recommend a specific fix
Quick wins (GSC required):
- Pages with >100 impressions but <3% CTR
- Action: rewrite title tag and meta description to improve click-through
Ranking opportunities (GSC required):
- Queries where average position is 5-20 (close to page 1 but not there)
- Action: strengthen the targeting page with better keyword placement, internal links, content depth
Content gaps (cross-reference with strategy document):
- High-priority gaps listed in the strategy document that have not been published yet
- Guides past their target publication date from the suggested future publishing schedule
- Keywords from guide frontmatter with no GSC impressions (not indexed or not ranking)
- GSC queries with impressions but no matching guide (content to create)
- Keyword clusters with weak strength ratings that need supporting content
Strategy compliance:
- Are guides hitting the benchmark of 100+ organic sessions/month within 90 days of publication?
- Are primary keywords ranking page 1 within 90 days?
- Are the recommended internal links from the strategy document actually in place?
- Is the publishing cadence on track with the suggested schedule?
- Are there content refresh triggers firing (ranking drops, traffic declines, product updates)?
Anti-Sludge Rules
- Be specific with numbers. "Sessions increased 34% week-over-week" not "traffic is doing well"
- Name the actual guide, URL, and metric in every recommendation
- Diagnose specific problems: "bounce rate is 72% suggesting the title promises something the content does not deliver" not "engagement could be improved"
- Recommendations must be actionable: "Rewrite the meta description from X to Y" not "consider improving SEO"
- No generic praise. If something is working, explain the specific mechanism.
- No em dashes. Use commas, periods, or parentheses.
- No AI cliches (revolutionize, game-changer, unlock, supercharge, seamlessly, cutting-edge)
Step 5: Score Maturing Hypotheses
Read the SEO hypothesis ledger at reports/seo/data/hypothesis-ledger.md.
For each row where status = IN-FLIGHT and window_end <= today:
- Look up the current value of
metric from today's analytics/GSC data
- Compare to
baseline and the hypothesis target
- Append a new row with the result and status (PASS/FAIL/INCONCLUSIVE)
- If PASS, note the winning tactic for the strategy master section 4
- If FAIL, note the dead hypothesis for section 5
For new insights discovered during analysis (e.g., a new high-impression low-CTR page), consider logging a new hypothesis with the next available S-### ID.
Step 6: Generate and Save Report
Save the report to:
reports/seo/daily/YYYY-MM-DD/daily-seo-brief.md
Use today's date in the filename.
Report Structure
# SEO Monitor Report
**Date:** {YYYY-MM-DD}
**Period:** Last 7 days (compared to previous 7 days)
**Guides published:** {N} total | {N} public
**GSC data:** Available | Not configured
---
## Traffic Summary
| Metric | This Week | Last Week | Change |
|--------|-----------|-----------|--------|
| Total sessions | {N} | {N} | {+/-N%} |
| Unique visitors | {N} | {N} | {+/-N%} |
| Organic search sessions | {N} | {N} | {+/-N%} |
| Avg engagement rate | {N%} | {N%} | {+/-N%} |
## Traffic Sources
| Source | Sessions | % of Total |
|--------|----------|-----------|
| Organic search | {N} | {N%} |
| Direct | {N} | {N%} |
| Referral | {N} | {N%} |
| Social | {N} | {N%} |
---
## Search Console Performance
(Include only if GSC is configured)
### Top Search Queries
| Query | Impressions | Clicks | CTR | Avg Position |
|-------|------------|--------|-----|-------------|
| {query} | {N} | {N} | {N%} | {N} |
### Quick Wins (high impressions, low CTR)
| Page | Query | Impressions | CTR | Position | Action |
|------|-------|------------|-----|----------|--------|
| {slug} | {query} | {N} | {N%} | {N} | {Specific fix} |
### Ranking Opportunities (position 5-20)
| Query | Page | Position | Impressions | Action |
|-------|------|----------|------------|--------|
| {query} | {slug} | {N} | {N} | {Specific action} |
---
## Top Performers
### 1. {Guide Title}
- **URL:** /guides/{slug}
- **Sessions:** {N} ({+/-N%} wow)
- **Engagement:** {reader|engaged} ({N%} read >50%)
- **Top source:** {organic|referral|direct}
- **GSC:** {N} clicks, {N} impressions, position {N} for "{top query}"
- **Why it works:** {Specific analysis of what makes this content successful}
(repeat for top 5)
---
## Underperformers
### 1. {Guide Title}
- **URL:** /guides/{slug}
- **Sessions:** {N} ({+/-N%} wow)
- **Engagement:** {bounce|skimmer} ({N%} bounce rate)
- **Keywords targeted:** {from frontmatter}
- **GSC:** {N} impressions but {N} clicks (CTR {N%})
- **Problem:** {Specific diagnosis}
- **Recommendation:** {Specific, actionable fix}
(repeat for bottom 5)
---
## Content Inventory Status
| Category | Guides | Avg Sessions/Week | Avg GSC Position | Trend |
|----------|--------|-------------------|-----------------|-------|
| claude-code | {N} | {N} | {N} | {up/down/flat} |
| plugins | {N} | {N} | {N} | {up/down/flat} |
| mcp | {N} | {N} | {N} | {up/down/flat} |
| content | {N} | {N} | {N} | {up/down/flat} |
---
## Strategy Compliance
**Source:** reports/seo/seo-strategy-master.md
### Publishing Schedule
| Planned Guide | Target Date | Status |
|--------------|-------------|--------|
| {Guide from strategy} | {date} | Published | On track | Overdue |
### Benchmark Tracking
| Guide | Days Since Published | Organic Sessions/Month | Target (100+) | Status |
|-------|---------------------|----------------------|---------------|--------|
| {slug} | {N} | {N} | {met/not met} | {on track/at risk/missed} |
### Missing Internal Links
| From | To | Recommended By Strategy | Status |
|------|----|------------------------|--------|
| {slug} | {slug} | Yes | Missing | Present |
### Cluster Health
| Cluster | Pillar Page | Guides | Strength | Change |
|---------|------------|--------|----------|--------|
| Claude Code | claude-code-daily-workflows | {N} | {Strong/Moderate/Weak} | {improved/stable/declined} |
| MCP | claude-code-mcp-servers-extensions | {N} | {Strong/Moderate/Weak} | {improved/stable/declined} |
| Marketplace | getting-started-anthropic-marketplace | {N} | {Strong/Moderate/Weak} | {improved/stable/declined} |
| Agent SDK | build-custom-claude-agent | {N} | {Strong/Moderate/Weak} | {improved/stable/declined} |
| Enterprise | enterprise-claude-code-managed-settings | {N} | {Strong/Moderate/Weak} | {improved/stable/declined} |
---
## Opportunities
1. **{Topic}**: {Why this is an opportunity, citing specific data from analytics or strategy gaps}
2. ...
## Prioritised Next Steps
1. [ ] {Highest impact action with specific guide/content}
2. [ ] ...
3. [ ] ...
4. [ ] {Content to create}
5. [ ] {Content to update}
Step 7: Append Daily Metrics and Update Strategy Master
Append to seo-metrics.jsonl
After generating the report, append a single JSON line to reports/seo/data/seo-metrics.jsonl:
{"date":"YYYY-MM-DD","sessions_7d":N,"unique_users_7d":N,"organic_sessions_7d":N,"organic_share_pct":N,"gsc_impressions_7d":N,"gsc_clicks_7d":N,"gsc_avg_ctr_pct":N,"gsc_avg_position":N,"guides_published":N,"guides_indexed":N}
This creates an append-only time series for trend analysis across runs.
Update Strategy Master
Apply targeted diffs to reports/seo/seo-strategy-master.md:
- Update section 1 (Current Performance Snapshot) with this week's numbers
- Re-render section 3 (Active Hypotheses) from the hypothesis ledger
- Move any scored hypotheses to section 4 (Winning Tactics) or section 5 (Dead Hypotheses)
- Append a changelog entry to section 9
Never rewrite the strategy master wholesale. Only apply diffs, and always append to the changelog.
Quality Checklist
Before saving the report, verify: