topic-memory
Manages the living content-topics.json — CRUD operations on topics, scoring history, revisit scheduling, performance data integration, and archive management. Use when reading, adding, updating, or archiving topics in the content intelligence system.
| Model | Source |
|---|---|
| sonnet | pack: content-pumper |
Full Reference
┏━ 🧠 topic-memory ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ Living state layer for the content intelligence ┃ ┃ system — reads, writes, archives, and scores ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
topic-memory
Section titled “topic-memory”Manages content-topics.json — the single source of truth for the Topic Brain system. All other skills read from or write to this file via the operations below. Idempotent — safe to call repeatedly.
JSON Schema
Section titled “JSON Schema”{ "version": "1.0", "lastUpdated": "<ISO timestamp>", "config": { "autoThreshold": 80, "checkIntervalHours": 6, "maxQueueSize": 50, "scoringWeights": { "freshness": 0.20, "searchVolume": 0.20, "socialBuzz": 0.15, "polarizationHeat": 0.10, "brandFit": 0.20, "competitionGap": 0.15 } }, "topics": [ { "id": "<uuid>", "title": "<string>", "slug": "<string>", "category": "<string>", "verticals": ["<string>"], "score": 0, "signals": { "freshness": 0, "searchVolume": 0, "socialBuzz": 0, "trendVelocity": "rising | stable | falling", "polarizationHeat": 0, "brandFit": 0, "competitionGap": 0 }, "sentiment": { "polarized": false, "sides": [ { "label": "<string>", "position": "<string>", "estimatedSize": 0, "triggers": ["<string>"], "engagement": 0 } ] }, "history": { "firstSeen": "<ISO timestamp>", "lastScored": "<ISO timestamp>", "lastWritten": "<ISO timestamp | null>", "timesWritten": 0, "nextCheck": "<ISO timestamp>", "articles": [ { "url": "<string>", "publishedAt": "<ISO timestamp>", "site": "<string>", "wordCount": 0 } ], "performance": [ { "recordedAt": "<ISO timestamp>", "source": "ga4 | gsc", "sessions": 0, "clicks": 0, "impressions": 0, "avgPosition": 0, "ctr": 0 } ] }, "status": "discovered | queued | writing | published | monitoring | archived", "targetMode": "evergreen | trending | seasonal", "targetSites": ["<string>"] } ], "archive": []}Archive entries use the same structure as topics.
Operations
Section titled “Operations”| Operation | Input | Action |
|---|---|---|
add-topic | title, category, verticals, targetMode, targetSites | Append to topics[] with uuid, default signals (all 0), status=discovered |
update-score | topicId | Recalculate composite score from signals using config.scoringWeights |
update-signals | topicId, signals patch | Merge signal values, then call update-score |
update-sentiment | topicId, sentiment object | Replace sentiment block, set lastScored |
record-article | topicId, url, site, wordCount | Push to history.articles, increment timesWritten, set lastWritten, call set-next-check |
record-performance | topicId, source, metrics | Push to history.performance[] |
set-next-check | topicId | Set nextCheck = now + config.checkIntervalHours |
archive-topic | topicId | Move from topics[] to archive[], set status=archived |
get-leaderboard | — | Return topics[] sorted by score descending |
get-due-for-check | — | Return topics[] where nextCheck < now |
Status Lifecycle
Section titled “Status Lifecycle”discovered └─▸ queued (score ≥ autoThreshold, within maxQueueSize) └─▸ writing (assigned to content-pumper) └─▸ published (article recorded in history.articles) └─▸ monitoring (tracking performance post-publish) └─▸ archived (auto-archive rules triggered)Scoring Formula
Section titled “Scoring Formula”Normalize each signal to 0–100 before weighting:
composite = Σ(normalized_signal × config.scoringWeights[signal_name])Signals mapped to weights:
freshness× 0.20searchVolume× 0.20socialBuzz× 0.15polarizationHeat× 0.10brandFit× 0.20competitionGap× 0.15
trendVelocity is categorical (rising | stable | falling) — not included in the weighted sum but used in auto-archive rules and tie-breaking.
Auto-Archive Rules
Section titled “Auto-Archive Rules”Archive a topic when ALL three conditions are true:
history.timesWritten > 3signals.trendVelocity === "falling"signals.socialBuzz < 0.2
Call archive-topic for any topic matching all three. Log the trigger in a note field on the archived entry.
File Location
Section titled “File Location”Default path: content-topics.json at project root.
Override via env var CONTENT_TOPICS_PATH. Always read the full file, mutate in memory, write back atomically. Never partial-write.
Integration
Section titled “Integration”| Skill | Reads | Writes |
|---|---|---|
trend-scanner | config.checkIntervalHours | update-signals, add-topic |
topic-scorer | topics[], config.scoringWeights | update-score |
sentiment-mapper | topics[] | update-sentiment |
content-pumper-pimp | get-leaderboard, get-due-for-check | record-article, status transitions |
content-scheduler | get-leaderboard | set-next-check |