# Content Refresh

> Proactively identifies declining content and orchestrates refreshes. Use when detecting content decay, generating refresh briefs, tracking refresh effectiveness, or auditing content health. Triggers on "content refresh", "content decay", "declining content", "refresh audit", "content health check", or when user asks about underperforming/stale content.

- Skill: `lcrvl2/content-refresh` (Agent Skill, multi-file: 8 files)
- Install (CLI): `npx skillmds@latest add lcrvl2/content-refresh`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lcrvl2/content-refresh/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: lcrvl2 (https://skillmd.com/u/lcrvl2)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lcrvl2/content-refresh

---


# Content Refresh Skill

Detects content decay, prioritizes refresh candidates, generates briefs, and tracks refresh effectiveness.

**Universal rule**: no company, domain, or GSC property is ever hardcoded. Everything client-specific comes from `clients/<slug>/client.json` and the client context. GSC is the PREFERRED signal source, never a prerequisite: the skill degrades gracefully through the signal cascade below.

## Commands

| Command | Purpose |
|---------|---------|
| `/content-refresh <client>` | Dashboard: queue, recent refreshes, tracking |
| `/content-refresh scan <client>` | Run decay scan (manual trigger) |
| `/content-refresh queue <client>` | Show prioritized refresh queue |
| `/content-refresh <client> [url]` | Check specific URL's refresh status |
| `/content-refresh report <client>` | Effectiveness report |

---

## Client Parameters (resolve FIRST)

From `clients/<slug>/client.json`: `domain`, `url`, `gsc_property`, `language`. Data reserve: `clients/<slug>/data/` (crawl + DataForSEO baseline written by seo-collect). Outputs: `clients/<slug>/outputs/refresh/`.

---

## Signal Sources (cascade, never abort)

| Priority | Source | Signals available | Condition |
|---|---|---|---|
| 1 | **GSC, via seo-monitoring** (`outputs/monitoring/`: latest `gsc-*.json` export, snapshots, history; direct gsc-mcp query ONLY if monitoring has never run) | clicks, impressions, CTR, positions per page (the full detection) | GSC access connected for this client |
| 2 | **DataForSEO** (client's data reserve `data/dataforseo/`, or labs `ranked_keywords`/`historical_rank_overview` live) | positions and their evolution per page | DataForSEO creds |
| 3 | **Crawl heuristics** (data reserve `data/crawl/`, or sitemap) | age (lastmod), dated content (year in URL/title/body), thin pages, content-type schedules | always available |

Announce which sources are active at scan start. With source 3 only, the scan still produces a useful queue (age + dated-content + schedule triggers); score-based traffic detection is marked "pending GSC connection".

**Division of labor with seo-monitoring**: monitoring SUMMARIZES performance (what happened) and is the ONLY skill that fetches GSC; THIS skill analyzes what can be optimized. All optimization detection (decay scoring, stagnation, thresholds) is computed HERE, from the data monitoring deposits (`outputs/monitoring/`: gsc exports, snapshots, history.json). Never re-fetch what monitoring already deposited; never expect monitoring to pre-compute signals.

---

## Workflow Overview

```
SCAN → DETECT → PRIORITIZE → SYSTEM/LEGACY CHECK → CLASSIFY → BRIEF ROUTING → QUEUE
```

---

## Tools (harness-aware)

**In Claude Code**, load via ToolSearch at workflow start (per available source):
```
ToolSearch: "select:mcp__gsc-mcp__get_search_analytics"        # source 1, ONLY if no monitoring export exists
ToolSearch: "select:mcp__gsc-mcp__get_search_by_page_query"    # source 1, same condition
ToolSearch: "select:mcp__dataforseo__serp_organic_live_advanced"  # optional, competitor positions
```
**In other harnesses**: use the equivalent tools if exposed. Existing-content crawl uses crawl4ai (`POST /md`), fallback WebFetch.

**Fallbacks**: GSC unavailable → drop to source 2, then 3 (NEVER abort). DataForSEO unavailable → source 3. crawl4ai unavailable → proceed without content-level baseline.

---

## Stage 1: Scan

**Input**: the client's URL inventory, from the best available source:
1. The latest monitoring export (`outputs/monitoring/gsc-*.json`, `pages` section) when present; otherwise direct GSC query (90 days + 30 days, 150 rows, `site_url` = client's `gsc_property`)
2. Otherwise: the client's crawl reserve (`data/crawl/<date>/`) or sitemap

Apply exclusion rules (see [references/decay-thresholds.md](references/decay-thresholds.md)): non-content subdomains, non-article paths, utility pages.

---

## Stage 2: Detect

Two parallel detection paths:

### Path A: Score-Based Detection (requires source 1 or 2)

For each URL, calculate decay score using weighted signals with guardrails.

**Traffic calculation** (GSC):
```
previous_30d_avg = (90d_total - last_30d) / 2
change_pct = (last_30d - previous_30d_avg) / previous_30d_avg * 100
```

**Guardrails** (skip signal if not met):
- Traffic drop: only if baseline ≥20 clicks/month
- Position drop: only if starting position ≤20

With source 2 only: position-based signals only (position drop, stagnation), no traffic signals.
Full threshold details: [references/decay-thresholds.md](references/decay-thresholds.md)

### Path B: Hard Triggers (works with source 3 alone)

Bypass scoring for:
- Age >12 months (crawl lastmod / publication date)
- **Dated content**: year in URL, title, or body data older than current year (e.g. a "[topic] 2024" page in 2026)
- Content-type schedules (comparison: quarterly, pricing: monthly, statistics: quarterly)
- Cluster trigger (the cluster's money page or pillar was refreshed)

---

## Stage 3: Prioritize

**Priority Score** = (Traffic Value × 0.5) + (Decay Severity × 0.5)

**Traffic Value** factors: peak monthly clicks (40%), keywords ranking count (30%), conversion potential/intent (30%). Without traffic data (source 3 only): rank by business value of the cluster (money-page proximity per `linking-clusters.md`) + trigger severity.

Sort by priority score, take top N (default: 20).

---

## Stage 4: Classify

### 4a. System/Legacy Detection (Pre-Check)

Determine whether the content was produced by this system or predates it:

1. Extract the slug from the URL
2. Search the client's outputs: `clients/<slug-client>/outputs/content/*/` for a brief or article matching the slug
3. **Match found** → system content (prior brief + metadata exist)
4. **No match** → legacy content (pre-system, no prior context)

Legacy is a **flag**, not a tier override: the decay score still determines the tier; legacy only changes the brief generation method.

### 4b. Tier Classification

| Tier | Trigger | System Content Action | Legacy Content Action |
|------|---------|----------------------|----------------------|
| **Auto-fix** | Only broken links OR stale proof points | Direct edit, no brief | Direct edit, no brief |
| **Partial Update** | 50-69% decay OR age-only trigger | Refresh brief ([refresh-brief-template.md](references/refresh-brief-template.md)) | `/content-brief --refresh [url]` |
| **Full Rewrite** | ≥70% decay | `/content-brief --refresh [url]` | `/content-brief --refresh [url]` |
| **Consolidation** | Multiple thin/overlapping pages on one topic | `/content-brief --refresh [url]` + redirect plan | `/content-brief --refresh [url]` + redirect plan |

**Why legacy Partial Updates use full briefs**: the thin refresh template assumes prior system context. Legacy content has none, so it benefits from the full research of `/content-brief --refresh`.

---

## Stage 5: Generate Brief (Routing Fork)

### Path A: Refresh Brief Template (Partial Update, system content only)

Template: [references/refresh-brief-template.md](references/refresh-brief-template.md). Refresh briefs include all fields Article-creator requires (keyword, format, word count, outline, CTAs, links) and can be fed directly to `/article-create`.

### Path B: Full Brief via Content Brief Creator (Full Rewrite, Consolidation, Legacy Partial Update)

Invoke `/content-brief --refresh [url]` (full 11-stage workflow with refresh modifications: skips cannibalization, crawls existing article as baseline, dual info-gain comparison, reuses slug, appends Refresh Context). See [Content-brief-creator/SKILL.md](../Content-brief-creator/SKILL.md).

### Path C: Auto-fix (no brief)

Direct edits: replace broken links; update stale proof points from the CLIENT's `context/proof-points.md` (never from any other source). Log changes to `outputs/refresh/auto-fixes.log`.

---

## Stage 6: Refresh Queue (orchestrator handoff)

Write the prioritized queue to `clients/<slug>/outputs/refresh/queue.json`, one entry per candidate:

```json
{
  "url": "...",
  "slug": "...",
  "refresh_tier": "partial_update",
  "is_legacy": false,
  "decay_score": 65,
  "signal_sources": ["gsc"],
  "baseline_metrics": { "position": 8, "clicks_30d": 450 },
  "decay_signals": ["position_drop", "age"],
  "brief_method": "refresh_template"
}
```

**`brief_method` values**: `"refresh_template"` | `"full_brief_refresh"` | `"auto_fix"`.

The queue is the handoff point for whatever orchestrates the pipeline (human, or an orchestrator agent). This skill does not manage a board. Downstream flow: BRIEF → REDACTION → QA (Content-quality-evaluator) → PUBLISH.

---

## Stage 7: Track Effectiveness

Capture baseline at refresh completion, snapshot at T+30/T+60/T+90 (requires source 1 or 2; with source 3 only, mark tracking "pending GSC connection").

**Categories**: Recovery (≥+10%), Stable (-10% to +10%), Failed (≤-10%).
Save to `clients/<slug>/outputs/refresh/tracking.json`.

---

## Output Files (per client)

| File | Purpose |
|------|---------|
| `clients/<slug>/outputs/refresh/scan-YYYY-MM-DD.json` | Scan results |
| `clients/<slug>/outputs/refresh/queue.json` | Current prioritized queue |
| `clients/<slug>/outputs/refresh/tracking.json` | Effectiveness tracking |
| `clients/<slug>/outputs/refresh/auto-fixes.log` | Minor auto-fixes log |
| `clients/<slug>/outputs/content/<date>/content-brief-[slug]-refresh-*.md` | Refresh briefs (via CBC) |

---

## Stagnation Signal

Computed by the scan from the monitoring history (`outputs/monitoring/history.json`, positions over time) or GSC data. Content stable at position 7+ for 60+ days warrants optimization investigation:

| Position | Action |
|----------|--------|
| #7-10 stable | Flag for SERP analysis |
| #11-15 stable | Flag for optimization |
| #16-20 stable | Lower priority, monitor |

**Investigation checklist**: what do #1-6 have that this page doesn't? Missing sections? E-E-A-T gaps? Missing SERP features (FAQ, video)?

