## TL;DR

Never trust one timestamp-free payload. Cross-check freshness before the agent acts on it: require a recency field, compare against a second source on high-stakes fields, and expire cached rows aggressively. Stale data fails silently, which is why it is worse than an error.

## The query

```text
agent's unsubscribed-contact check ran against a 12-hour-stale cached list  -  it emailed 90 opted-out leads
```

## Use this when

- the agent acted on data that turned out to be months or years old
- cached rows have no date or the provider's cache is known to lag

## Not for

- data that is wrong at the source (that is a provider-quality problem)
- schema changes that break parsing (see the parsing skill)

## Steps

### Step 1: Find where the stale value entered the pipeline

Trace the field back: which provider, which cache layer, which row. Check the payload for a date or updated-at field. If there is none, that is the root cause: the agent cannot tell fresh from rotten.

**Expected: the exact cache or provider row that served the stale value, with its age.
### Step 2: Quarantine the affected outputs

List every message, decision, or write the agent made from the stale value. Pause further sends that depend on that field until the data is refreshed. Stale-data incidents widen while the agent keeps running.

**Expected: a list of affected leads/messages; dependent steps paused.
### Step 3: Refresh from source and add a freshness gate

Re-pull the data from the authoritative source and require a recency check before use: reject rows older than your threshold (e.g. 30 days for employment data) or flag them for human review. No date on the payload means treat it as suspect by default.

**Expected: refreshed rows with verified dates; the gate rejects or flags stale rows.
### Step 4: Add a second-source check for high-stakes fields

For fields that change outcomes (job title, company status, employee count), compare two providers and flag disagreements instead of picking one silently. Disagreement is a signal, not noise.

**Expected: mismatched fields raise a review flag instead of flowing into messages.
### Step 5: Shrink the cache TTL for volatile fields

Employment, funding, and headcount data rot fast. Set TTLs in days, not months, for volatile fields, and log cache age on every read so the next incident shows its age immediately.

**Expected: cache reads carry an age; nothing older than the TTL is served.

## Why it happens

Enrichment and cache layers serve old snapshots without saying so. Agents treat the payload as current because nothing in it says otherwise, so a 2021 funding round reads as 'recent' and a dead company reads as a prospect.

## Edge cases

- Two providers can both be stale; agreement is not proof of freshness.
- Replication lag makes 'just written' data look stale; distinguish lag from rot by checking the writer.
- Over-aggressive TTLs burn enrichment credits; tier TTLs by field volatility.

## Finding this skill

This skill was written for sales-outreach (SDR) agents hitting the failure above. Search Vectle for the exact error text or symptom to find it and related skills.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_M_i8WwiNCtHOqGMiCvCsYw
