topic-momentum

$npx mdskill add aaronjmars/aeon/topic-momentum

> **${var}** — Optional domain filter (e.g. "crypto", "AI", "prediction-markets"). If empty, scans every domain declared in `memory/topics/content-domains.md`.

SKILL.md
.github/skills/topic-momentumView on GitHub ↗
---
name: topic-momentum
description: Weekly content-gap scanner — cross-references rising narrative signals (narrative-tracker, tweet-roundup, paper-pick, etc.) against recent article history and surfaces the top 3 uncovered angles to write next.
var: ""
tags: [content, meta]
---
> **${var}** — Optional domain filter (e.g. "crypto", "AI", "prediction-markets"). If empty, scans every domain declared in `memory/topics/content-domains.md`.

Today is ${today}. Read `memory/MEMORY.md` before starting.

## Voice

If `soul/SOUL.md` and `soul/STYLE.md` are populated, read both and match the operator's voice when drafting the suggested hook line (step 4) and the notification body. If they are empty templates or absent, use a clear, direct, neutral tone — short, declarative, position-first.

## Why this skill exists

The article skill picks one trending topic per run. Skills like `narrative-tracker`, `tweet-roundup`, and `paper-pick` surface discrete signals. Nothing cross-references **what's been covered** against **what keeps surfacing** — so timely angles get missed or covered weeks late. This skill closes that gap: a weekly pattern detector that finds the signal the operator keeps receiving but hasn't written about yet.

## Config

Domain filters and signal-source aliases live in `memory/topics/content-domains.md`. If the file doesn't exist, create the seed below and continue with the default (no filter):

```markdown
# Content Domains

## Domains
- crypto
- AI
- prediction-markets
- macro
- protocols

## Signal Sources
(Skill log section names that produce candidate signals. Add more as you wire up trackers.)

- narrative-tracker     # rising/peaking narratives
- tweet-roundup         # topic-grouped tweet picks
- paper-pick            # research papers
- repo-actions          # GitHub-ecosystem ideas

## Topic Memory Files
(Files in memory/topics/ that hold cross-run context the gap scanner should also read.)

- market-context.md
- papers.md
```

If `${var}` is set, restrict the gap-scan to themes that match the named domain (substring/keyword match against the theme name).

## Steps

### 1. Load recent article coverage

Use Glob to list `.md` files in `articles/` modified in the last 30 days (filename pattern `YYYY-MM-DD.md` makes this easy).

For each file:
- Read the H1 and first 2 sentences — extract the core topic and angle
- Note the date from the filename

Build a **covered-topics list**: `[{ date, topic, angle }]`.
- Articles ≤ 7 days old: "very recent" → suppress re-suggestion (-5 in scoring)
- Articles 8–14 days old: "recent" → penalize (+1 only)
- Articles 15–30 days old: still penalized lightly (+3)
- Articles > 30 days old or never written: full credit (+5)

### 2. Load narrative signals from recent logs

Read `memory/logs/` for the last 7 days (Glob `memory/logs/*.md`, sort by name, take last 7).

From each daily log, extract entries under each `Signal Source` declared in `content-domains.md`. For each entry, extract:
- The theme / narrative name
- Whether it was labeled "rising", "peaking", or otherwise high-signal
- How many sources / days surfaced it

Also read each `Topic Memory File` from `content-domains.md` (default: `memory/topics/market-context.md`, `memory/topics/papers.md`) for current macro themes and hot narratives.

Build a **signal-map**: `{ theme: { frequency_score, source_list, first_seen, last_seen } }`.

If `${var}` is set, filter signal-map to themes matching that domain.

### 3. Score the gaps

For each theme in signal-map:

| Criterion | Points |
|---|---|
| Surfaced 5+ days/sources in last 7d | +5 |
| Surfaced 3–4 days/sources | +3 |
| Surfaced 1–2 days/sources | +1 |
| Never written about | +5 |
| Last covered 15+ days ago | +3 |
| Last covered 8–14 days ago | +1 |
| Last covered in past 7 days | −5 (suppress) |
| Domain-fit: matches a declared domain in content-domains.md | +1 |

**Max score: ~14.** Drop themes with net score < 2.

Rank descending. Pick top 3.

### 4. Develop the angles

For each top-3 gap:
- Define a **specific angle** — not "write about X" but "X from the angle of Y; the thing everyone's missing is Z"
- Draft a **hook line** (voice per the Voice section above)
- Note **what triggered it** (sources from step 2)
- Note **last coverage** date or "never"

### 5. Update memory

Write `memory/topics/content-gaps.md` (overwrite):

```markdown
# Content Gaps — Last Updated: ${today}

## Top 3 Angles (Ranked by Signal Score)

### 1. <Theme Name> — Score: N/14
**Angle:** <specific take — not generic>
**Hook:** <suggested opener>
**Sources:** <what surfaced this, e.g. "narrative-tracker 4d, tweet-roundup 3d">
**Last coverage:** <date or "never">

### 2. <Theme Name> — Score: N/14
...

### 3. <Theme Name> — Score: N/14
...

---
*Generated by topic-momentum on ${today}. Consumed by: article skill, remix-tweets.*
```

### 6. Notify

Use Write to create `.pending-notify-temp/topic-momentum-${today}.md`:

```
topic momentum — ${today}

3 angles with high signal, no recent article:

1. <theme name> — <angle in one line>
2. <theme name> — <angle in one line>
3. <theme name> — <angle in one line>

full breakdown: memory/topics/content-gaps.md
```

Then run:

```bash
./notify -f .pending-notify-temp/topic-momentum-${today}.md
```

Keep total under 800 chars. Do NOT use `./notify "$(cat ...)"` — write the file first, pass the path.

### 7. Log

Append to `memory/logs/${today}.md`:

```markdown
## Topic Momentum
- **Themes scanned:** N
- **Gaps scored:** N
- **Top 3:** <theme1>, <theme2>, <theme3>
- **Lowest gap score included:** N/14
- **Updated:** memory/topics/content-gaps.md
- **Notification:** sent
- TOPIC_MOMENTUM_OK
```

If fewer than 3 scoreable gaps were found: log `TOPIC_MOMENTUM_SKIP: insufficient signal (<3 themes above threshold)` and stop without notifying.

## Required Env Vars

None. All reads from local `memory/` and `articles/` dirs. No external API calls, no curl, no prefetch script needed.

## Sandbox Note

No network calls required — all data comes from local memory files written by other skills. If `memory/logs/` is sparse (e.g. first run), fall back to reading the `Topic Memory Files` declared in `content-domains.md` directly as the signal source. **WebSearch** is available as a last resort for current narrative heat if local data is too thin, but should rarely be needed.
More from aaronjmars/aeon