name: topic-momentum description: 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):
# 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):
# 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:
./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:
## 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.