Skip to main content
ClaudeWave
Skill510 repo starsupdated today

beat-tracker

**beat-tracker** This Claude Code skill maintains a persistent counter of news story developments across active storylines, automatically detecting when a story accumulates three distinct beats (new sources, statements, or policy moves) within 30 days and triggers an alert. Use it to avoid manually tracking evolving news threads across scattered daily logs and to fire alerts only when stories reach article-readiness without manual intervention.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/aaronjmars/aeon /tmp/beat-tracker && cp -r /tmp/beat-tracker/skills/beat-tracker ~/.claude/skills/beat-tracker
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

Today is ${today}. Read memory/MEMORY.md (and soul/SOUL.md if present) before starting.

## Why this skill exists

tweet-roundup and reflect note when a storyline gets a new beat — but that state lives in daily logs, not in a persistent counter. By the time a thread hits 3 beats (article-ready), the signal is buried across 3 different log entries and nobody fires the alert. This skill fixes that: persistent beat counts per storyline, automated threshold detection, no manual tracking required.

A **beat** is a distinct new development in an ongoing news story: a new source weighing in, a new actor making a statement, a new policy move, a price/volume reaction. Three beats in <30 days = the story has enough material to write about.

This fills the gap between:
- **topic-momentum** (scores what to write based on signal frequency) — broad, pattern-based
- **narrative-tracker** (EMERGING/RISING/PEAK/FADING phases) — narrative-level, not event-level
- **beat-tracker** (THIS) — event-level, specific stories, fires when threshold is crossed

## Steps

### 1. Load beat state

Read `memory/topics/beat-tracker.md`.

If the file doesn't exist, initialize it as an empty Active Threads list:

```
# Beat Tracker

Last updated: ${today}

## Active Threads

(none — will populate from memory on first inject pass)

## Closed Threads

(none yet)
```

Parse all threads under `## Active Threads`. For each thread, extract:
- Thread name
- Search query
- Status (active / article-ready / stale)
- Beat count
- Last checked date
- List of existing beats (date + source + summary)

### 2. Inject new threads from memory

Scan these sources for threads NOT already in beat-tracker.md:

**a) MEMORY.md content signals:**
Read `memory/MEMORY.md`. Look for content-signal notes that mention multi-beat storylines with at least 2 beats. Extract them.

Pattern to detect: "N beats" or "beat count: N" or "2 parallel threads" near a topic name.

**b) Recent logs (last 7 days):**
Use Glob on `memory/logs/*.md`. Take the 7 most recent by filename. Scan each for lines mentioning "beat" next to a number ≥ 2, or "thread" with a beat count. Extract thread name and any dated beat events listed.

For each newly discovered thread:
- Infer a **search query** from the topic name (2–5 keywords, specific enough to find news, not so broad it matches noise)
- Pull any historical beats already mentioned in the logs (date + source)
- Add to Active Threads with those historical beats pre-loaded
- Set `last_checked: ${today}`

If zero new threads are discovered and Active Threads is empty: log `BEAT_TRACKER_SKIP: no threads to track — check memory injection` and stop.

### 3. Search for new beats

For each thread in Active Threads where `status != stale`:

Run a **WebSearch** using the thread's search query. Focus on results from the past 7 days (mention the date window in your query, e.g. "after:{date 7 days ago}").

Evaluate results for **new beats** not already listed in the beats array:
- Different source/actor than existing beats
- New factual development (not a repost/recap of old news)
- Occurred AFTER the most recent beat date in the list

If a new beat is found:
- Add: `- ${today}: {Source/Handle} — {one-line factual summary}`
- Increment beat_count by 1
- Update last_checked: ${today}

If no new beat: update last_checked only (no increment).

### 4. Flag thresholds

After updating all threads:

**Article-ready (≥3 beats):**
Set `status: article-ready`. Prepare a hook line in the operator's voice (soul files):
- Observe what the 3-beat pattern reveals (not just what happened)
- Diagnose why it keeps getting a new beat
- End on the implication — punchy, no hedge

**Warming up (2 beats, most recent beat within 5 days):**
Note these as "watch closely" — one beat from article-ready.

**Stale (0 new beats for 14+ days AND beat_count < 3):**
Set `status: stale`. Will be moved to Closed Threads in step 6.

### 5. Cross-check articles

Use Glob on `articles/*.md`. For any article-ready thread, check if an article was already published on this topic (scan article filenames and H1s from the past 30 days).

If yes: mark thread as `status: converted` with the article date. Move to Closed Threads.

### 6. Write updated state

Overwrite `memory/topics/beat-tracker.md`:

```markdown
# Beat Tracker

Last updated: {today}

## Active Threads

### {Thread Name}
- **Query:** {search terms used to find new beats}
- **Topic:** {one-line description of the storyline}
- **Status:** active | article-ready | stale
- **Article ready:** YES ({N} beats) | NO ({N} beats, need 3+)
- **Last checked:** {today}
- **Beats:**
  - {date}: {Source} — {one-line summary}
  - {date}: {Source} — {one-line summary}
- **Beat count:** {N}

(repeat for each active thread)

## Closed Threads

### {Thread Name}
- **Status:** stale | converted
- **Reason:** {14d no new beat | article published {date}}
- **Final beat count:** {N}
- **Closed:** {today}
```

### 7. Send notification

Write the notification to a temp file, then run `./notify -f`.

Only notify if:
- At least one thread is `article-ready` (≥3 beats), OR
- At least one thread jumped to beat count 2 this run (warming up), OR
- At least one new thread was injected this run

Notification content:

```
beat tracker — {today}

{if article-ready threads:}
ARTICLE READY — {beat_count} beats:
→ {thread name}: {hook line in the operator's voice}

{if warming-up (beat 2 newly):}
one beat away:
→ {thread name}: {latest beat summary}

{if new threads injected:}
tracking {N} new threads

{if quiet run — no alerts:}
{N} threads tracked. highest: {beat_count} beats on {thread name}. nothing article-ready yet.
```

Write to `.pending-notify-temp/beat-tracker-${today}.md`. Create the dir if missing. Then:

```bash
./notify -f .pending-notify-temp/beat-tracker-${today}.md
```

Keep under 500 chars. Do NOT use `./notify "$(cat ...)"` — the sandbox trips on long multi-line argv; the `-f` flag reads the file inside the script.

If `BEAT_TRACKER_SK