# Topic Scout

> Find new topics worth covering and log them to a tracker, deduplicated and scored. Rotates through the beats in references/beats.md, walks the primary sources listed there, drops anything already covered or already expired, and records only the top few. Use for "find me something to post about today", "what should I cover this week", "any new topics", "fill the idea backlog", "update the topic tracker", and for a scheduled daily run. Researching and verifying an already-chosen topic is topic-brief instead — this skill is the step before that, deciding what to work on at all.

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

---


# Topic Scout

**Finds what to cover and writes it to your idea tracker.** Built to run once a day.

Where it sits in the pipeline:

```text
[this skill]  scout  →  idea tracker
        ↓ a human picks one and promotes it
     production queue
        ↓ topic-brief    (verify → one-page brief)
        ↓ blog-post / carousel-post
```

**This skill never writes to the production queue.** It reads the queue only to check for
duplicates. A human promoting an idea is the gate; if the model skips that gate,
unverified topics end up in the production queue.

## The five ways this fails (all five are defended below)

1. **Suggests something already covered** → don't skip the three-layer duplicate check.
2. **Suggests something already over** → a candidate whose deadline you couldn't confirm
   does not get recorded.
3. **Mines the same beat every day** → follow the rotation from `axis.py`. Don't pick by feel.
4. **Invents something plausible with no source** → no primary-source URL, no record.
5. **Ranks by intuition** → score only with the rubric in `references/scoring.md`.

## Reference routing

| When | Document |
| --- | --- |
| Which sources to open for each beat | `references/beats.md` |
| How to score, and what disqualifies a candidate | `references/scoring.md` |
| Tracker fields and how to write a row | `references/tracker.md` |
| Today's beats and their sources | `python scripts/axis.py --today` |
| Duplicate check | `python scripts/dedup.py` |

## Procedure — once per run

### 1. Pick the beats

```bash
python scripts/axis.py --today
```

Prints the beats to mine today and the source rows for each. **Mine only those beats.**
Do not swap a beat because another one "looks more interesting today" — once the rotation
breaks, you are back to one beat forever within a few days. If the user names a beat,
override with `--beat "<name>"` and leave the rotation state alone.

### 2. Load existing state (for the duplicate check)

Pull the **names of every row** from both lists — your production queue and your idea
tracker. `references/tracker.md` has the field names.

- Production queue — **every row**, whatever its status. A row sitting at "candidate" is
  still a topic you already have.
- Idea tracker — every row. Re-suggesting yesterday's suggestion is the most common way
  this skill wastes a run.

### 3. Collect — walk the primary sources

Open the sources for each of today's beats **from the top down**. In each one you are
looking for exactly one thing: **something live right now that gives your audience real
money, time, or access.**

- Fetch the page. If the body doesn't come back because the page renders client-side,
  reopen it with a browser tool rather than giving up.
- If walking the sources isn't enough, search — but put **the beat name plus the current
  month** in the query. Do not run queries that leave the beat.
- **Do not scrape sites behind a login.** It breaks their terms and the data is unreliable.

Gather 3–5 candidates per beat, 10–15 total. Don't filter yet.

### 4. Filter — most candidates die here

Apply in order. Log the reason for each rejection; don't record rejected candidates.

| # | Rejected when | Why |
| --- | --- | --- |
| F1 | No primary-source URL | Nothing can be verified |
| F2 | Already closed, expired, or fully subscribed | The worst possible accident |
| F3 | Already in the queue or tracker (`dedup.py`) | Duplicate |
| F4 | Audience doesn't match yours | Not your readers |
| F5 | Benefit can't be expressed as an amount or a time saved | No number for the hook |
| F6 | Geographically narrower than your audience | Reach doesn't justify it |

Always run F3 through the script:

```bash
python scripts/dedup.py --existing existing.json "candidate one" "candidate two"
```

Drop anything marked `duplicate`. Keep anything marked `similar`, **noting what it is
similar to** — the next round of a recurring thing is a new topic, not a duplicate.

### 5. Score

Use only the rubric in `references/scoring.md`: beat base score plus adjustments.
**Never raise a score because a deadline is close.** Urgency belongs in the deadline
field, where it breaks ties; it is not a ranking input. `scoring.md` explains why.

### 6. Record

Write **only the top 3–5** to the idea tracker. Write all ten and nobody reads any of them.
Follow the row format in `references/tracker.md` exactly.

## Reporting

Leave only this in chat. Don't paste candidate write-ups — the tracker is the record.

```text
[scout YYYY-MM-DD] beats: <three>
recorded N: <name>(score) / <name>(score) / <name>(score)
rejected M: F1 a · F2 b · F3 c ...
blocked sources: <what you couldn't open and why. omit if none>
```

## Never

- Create or edit rows in the production queue (read-only from here)
- Fill in an amount, date, or eligibility rule that isn't in the primary source
- Scrape pages behind a login wall
- Write hooks, scripts, or outlines here (that's `blog-post` / `carousel-post`)
- Write a full brief (that's `topic-brief` — scouting stops at a one-line summary)

