# Personal Content Resurface

> Decide which saved personal content (saved YouTube videos, Instagram reels, WhatsApp self-messaged links/files, LinkedIn saved posts) should re-surface today, based on time-since-last-surface plus relevance to the user's current context. Returns a ranked list with reasons. Teaches three algorithms (SM-2, FSRS, custom context-aware hybrid) — the implementer picks at build time. Decision math only, not surfacing UX. Use when the user asks "what should I re-look at today", "resurface saved content", "spaced repetition for my saved stuff", "Connecting Dots prioritization", "second brain re-surfacing", "what saved content matters right now", "personalized content scheduling", or any variant of "I save things and never re-look at them — what should I look at now?"

- Skill: `build-with-dhiraj/personal-content-resurface` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add build-with-dhiraj/personal-content-resurface`
- Raw SKILL.md: https://api.skillmd.com/api/skills/build-with-dhiraj/personal-content-resurface/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: build-with-dhiraj (https://skillmd.com/u/build-with-dhiraj)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/build-with-dhiraj/personal-content-resurface

---


# Personal-content re-surface decision math

## What this skill does

Given (a) a corpus of saved personal content, (b) a static user profile, and (c) the user's recent activity, decide **which N items to re-surface today** and **why**. Output is a ranked list with one-line reasons. Does NOT define the UX (push notif vs daily digest vs dashboard widget) — that lives in product code.

## Inputs

| Signal | Shape | Source |
|---|---|---|
| **Corpus item** | `{id, source, captured_at, text/embedding, tags, last_surfaced_at, surface_count, useful_score}` | Obsidian vault, Pinecone, or Supabase — whatever the store is |
| **Static user profile** | `{interests[], role, current_projects[], stated_priorities[]}` | User-authored, lives in Obsidian or app DB |
| **Dynamic recent activity** | `{queries_this_week[], content_consumed_this_week[], current_focus_topic}` | Activity log of last 7 days |

## Output

```json
[
  { "item_id": "yt_abc123", "score": 0.87, "reason": "topic match with current focus + 14d since last surface" },
  { "item_id": "wa_xyz", "score": 0.81, "reason": "high useful_score + 60d dormant" }
]
```

Rank descending. Cap at N (default 5). Each item carries a one-line `reason` for transparency.

## Three algorithms — pick at build time

### 1. SM-2 (classic Anki) — day-1, no personalization needed

Decay-only. Each item has `interval` and `ease_factor`. When marked useful, `interval` grows. Re-surface when `now > last_surfaced_at + interval`. Zero LLM in the loop. Cheapest compute.

**Use when:** MVP day-1, corpus <100 items, no feedback signal yet.

### 2. FSRS (Free Spaced Repetition Scheduler) — modern, accurate

Three latent variables per item: difficulty, stability, retrievability. Updates via maximum-likelihood estimation. Outperforms SM-2 in benchmarks.

**Use when:** corpus >500 items AND ≥30 useful/not-useful events accumulated to train the model.

### 3. Custom context-aware hybrid — the Connecting-Dots moat

```
score = 0.5 × time_decay + 0.3 × relevance_to_recent_activity + 0.2 × static_profile_match
```

- `time_decay`: forgetting curve, sigmoid over days-since-surface
- `relevance_to_recent_activity`: cosine sim between item embedding and embedding of last 7d activity
- `static_profile_match`: cosine sim between item embedding and embedding of user profile

**Use when:** corpus >100 items AND user has authored a meaningful profile. **The recommended path for Connecting Dots specifically** — pure spaced-rep misses the "this is suddenly relevant" signal that recent activity unlocks.

See [references/algorithms.md](references/algorithms.md) for exact formulas, half-life tuning, and edge cases.

## Decision matrix

| Stage | Corpus | Feedback events | Algorithm |
|---|---|---|---|
| MVP day 1 | <100 | 0 | SM-2 |
| MVP weeks 2–4 | 100–500 | <30 | Custom hybrid (no FSRS training data yet) |
| Mature | 500+ | 30+ | FSRS + hybrid signal — ensemble |

## Common pitfalls

- **Cold start**: day-1 has no feedback. Default to SM-2 with `ease=2.5, interval=1`. Don't attempt FSRS.
- **Recency bias**: if only dynamic activity drives the score, old gold gets buried. The `time_decay` term fights this.
- **Echo chamber**: if `relevance_to_recent_activity` weights too high, the user only sees more-of-the-same. Cap that term at 0.5; force diversity sampling for top-N.
- **Surface fatigue**: enforce `min_days_between_surface = 7` regardless of score — same item every day is annoying.
- **Feedback bootstrap**: cold-start the `useful_score` to 0.5 (neutral), update on user signal. Never let it drop below 0.1 — that's bandit-style starvation.

## What this skill does NOT do

- Pull content from source platforms — that's `baoyu-youtube-transcript`, `integrate-whatsapp`, `superpowers-chrome:browsing`, etc.
- Store content — that's `obsidian-vault`, `memory-router`, or `supabase`.
- Extract entities/topics from content — that's `ner-content-pipeline`.
- Render the surface UX — that's product code (Obsidian dashboard, web app, push notif daemon).
- Build the RAG pipeline that answers queries — that's `rag-patterns`.

It only answers: "Given everything you have, what should I look at today?"

