# Trendy Detection

> Trendy detection stage (PRD §4.1). Fetches signals from all enabled sources (Google News RSS, SerpApi Google Trends, X API v2 trends, external_events), clusters them into candidate trends, and writes Trend Report documents to PocketBase. Daily cadence via Hermes gateway cron. Use when asked to run trend detection, "run detection now", or as the daily detection job.

- Skill: `guillaumemeyer/trendy-detection` (Agent Skill)
- Install (CLI): `npx skillmds@latest add guillaumemeyer/trendy-detection`
- Raw SKILL.md: https://api.skillmd.com/api/skills/guillaumemeyer/trendy-detection/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- Author: guillaumemeyer (https://skillmd.com/u/guillaumemeyer)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/guillaumemeyer/trendy-detection

---


# Trendy Detection

## Role

You are the Trendy trend radar. On each run you produce **candidate trends**
from live signals and persist them to PocketBase. You do **not** score, analyze,
or approve — later stages own those. Everything you write is idempotent:
re-running on the same day must not duplicate records.

## Workflow

### 1. Collect signals

Load the source skills and fetch raw signals:

- `trendy/google-news-rss` — key-free Google News RSS feed.
- `trendy/x-trends` — X API v2 `/trends/by/woeid` (requires `X_API_KEY`).
- SerpApi Google Trends — requires `SERPAPI_API_KEY`.
- `trendy/external-events-manager` — curated events from PocketBase
  `external_events` (key-free).

Sources with missing keys **degrade gracefully**: log the gap, continue with the
rest. A run that gets zero signals from every source is still a valid run —
record it and finish with `status=ok` (empty result), do not invent signals.

### 2. Cluster signals into trends

Group signals by keyword overlap (≥ 2 shared meaningful keywords). One signal
may belong to at most one cluster; leftover singletons are dropped unless they
are unusually strong (high volume + authoritative source). Each cluster becomes
a candidate trend with:

- `title` — short, human label (e.g. "Best Christmas gifts for family").
- `slug` — slugified title, unique.
- `source` = `detected`.
- `geography` + `geography_detail` inferred from signal geo.
- `identification_date` = today (America/Los_Angeles `YYYY-MM-DD`).
- `status` = `identified`.

Use `packages/shared` helpers (`clusterSignals`, `deriveTrendTitle`,
`slugify`) when available; otherwise follow the same rules by hand.

### 3. Write Trend Reports (status `identified → reported`)

For each candidate trend, write the Trend Report (zod `TrendReportSchema`):

| Field | Meaning |
|---|---|
| `category` | One primary category (fashion, gifting, decor, tech, fitness, pets, travel, food, …) |
| `tags` | 3–8 short tags |
| `signal` | The concrete signal observed (queries, feed items, volumes) with dates |
| `issue` | What problem/desire the trend expresses |
| `interpretability` | How understandable the trend is to a casual shopper |
| `rationale` | Why this is a trend now, evidenced |
| `sustainability` | Expected lifetime (fad vs durable) with reasoning |
| `vibe` | Emotional tone (cozy, playful, premium, …) |
| `flags` | `{copyright, political, adult, health, sensitivity}` booleans |

Evidence rules (anti-slop): every claim in `signal`/`rationale` must trace to a
fetched signal with a source ref and date. Never write "consumers are
increasingly…" without a source. Never recommend riding crisis/tragedy topics —
flag `sensitivity: true` and let the operator decide.

### 4. Persist

- Upsert `signals` records (dedup on `source_ref`).
- Upsert `trends` records (dedup on `slug`; update report if the trend already
  exists and is still in `identified`/`reported`).
- Advance status via the status machine (`identified → reported`) — use
  `packages/shared` `advanceTrendStatus`.
- Write one `agent_runs` row: `{stage: "detect", status: "ok", summary,
  metadata: {signals_fetched, signals_clustered, trends_written, degraded[]}}`.

## Completion contract (final output)

```text
skill: trendy-detection
status: ok|partial|failed
signals_fetched: N
trends_written: N
degraded: [source names]
agent_run_id: <pb_id>
```

`status=partial` when some sources degraded; `status=failed` only on hard errors
(PocketBase unreachable, fatal exception) — include `error` with one line.

## Pitfalls

- Do not cluster two signals that share only stopwords ("gift" vs "gifts" is
  fine, "the best" is not a shared keyword).
- Do not overwrite trends that have moved past `reported` — leave them alone.
- Do not attach URLs from unverified sources.
- Keep total run under the source budget in
  `marketing/trends-monitor/references/sources-and-budget.md`.

