Project Brief: Daily-5 News Digest

Status: MVP — Ready to Build Owner: You Tools: FreshRSS (self-hosted, admin-only), n8n, Google Sheets, Telegram, Obsidian, AI (Gemini/GPT) Related project: Video Summarizer (shares Telegram bot + Obsidian vault + security pattern)


1. Problem Statement

Existing RSS aggregation (FreshRSS) solves collection but not curation. Unread counts climb indefinitely, producing the same overwhelm this project exists to escape. The gap isn’t sources — it’s a missing forced-ranking layer that turns “everything” into “the 5 things worth your attention today,” scored against your own stated judgment, not a generic AI vibe-check.

2. Goal

One daily digest (Telegram + Obsidian note), containing exactly 5 items (title, source, link, one-line reason), drawn from all subscribed sources (YouTube, newsletters, blogs), scored using your manually-curated Google Sheet ratings as the primary signal. No backlog, no browsing, no second inbox to manage.

3. Non-Goals (MVP)

  • No archive/log of rejected items
  • No topic-relevance scoring beyond what’s captured in your sheet’s “Why” column
  • No automated reconciliation of new channels/blogs found via exploration — that’s Phase 2 (Section 9)
  • No manual FreshRSS browsing — it is admin-only, never a frontend

4. Design Principles

  1. FreshRSS is plumbing, not a product. It collects; it is never read directly.
  2. Your judgment is the primary scoring signal. The Google Sheet (H/M/L + Why) is a stronger prior than generic AI quality-scoring — the AI’s job is to apply your stated reasoning to today’s specific items, not invent taste from scratch.
  3. No memory of rejects. Items not in the top 5 are discarded permanently when marked read.
  4. Hard cap, always. Exactly 5 items daily, regardless of pool size or quality variance.
  5. One sheet, one source of truth. YouTube + blogs + newsletters all live in the same sheet structure (Channel/Source, Value H/M/L, Why, Status) so scoring logic doesn’t fork by content type.

5. Architecture — Workflow A: Daily Digest (MVP)

[Schedule Trigger] — once daily, e.g. 6am
        ↓
[Google Sheets node] — read subscriptions sheet (Channel, Value, Why, Status)
        ↓
[Code node] — build lookup table: {source_name → Value, Why}
        ↓
[HTTP Request] — pull unread items via FreshRSS API
        ↓
[Code node] — match each item to sheet lookup by source name; attach Value+Why as context; batch for AI
        ↓
[AI node] — score all items in one call, using Value+Why as primary signal + clickbait/depth as secondary
        ↓
[Code node] — parse AI JSON, sort by score, slice top 5
        ↓
[Telegram node] — send digest
        ↓
[Write File node] — write same digest as Obsidian note → `daily news digests/YYYY-MM-DD.md`
        ↓
[HTTP Request] — mark ALL originally-fetched items as read in FreshRSS

Node-by-Node Build Plan

#NodePurposeNotes
1Schedule TriggerDaily kickoff6am suggested; adjust to taste
2Google Sheets (Read)Pull subscription ratingsUse sheet URL directly; read all tabs if blogs/newsletters get a separate tab
3CodeBuild name→rating lookupNormalize names (lowercase, trim) for matching robustness
4HTTP RequestFreshRSS unread itemsGoogle Reader–compatible API; auth via API password
5CodeMatch + batchFuzzy match acceptable for MVP; unmatched sources default to “Unrated” (still eligible, no bonus weight)
6AI (Gemini/GPT)Score + rankPrompt in Section 7
7CodeParse, sort, top 5Error branch if malformed JSON
8TelegramSend digestReuse existing bot/chat ID security pattern
9Write FileObsidian noteSame vault mount as video summarizer project
10HTTP RequestMark all readOnly runs on success — failures don’t silently lose items

Error handling (minimum for MVP):

  • FreshRSS unreachable → Telegram alert, skip mark-as-read
  • Google Sheets unreachable → Telegram alert, skip run (don’t score blind)
  • AI call fails/unparseable → Telegram alert, skip mark-as-read

6. Google Sheet — Required Structure

Your existing sheet (YouTube-only) needs to extend to cover all source types before Workflow A can use it universally:

ColumnPurpose
Source NameMust reasonably match FreshRSS feed/channel title
TypeYouTube / Blog / Newsletter — helps you filter/sort manually later
Value (H/M/L)Your rating
WhyYour reasoning — fed directly into AI prompt as context
Statuse.g. “Review,” “Keep,” “Drop” — your existing pattern

Action for you: as you explore Substack, add new rows with Type = Blog, even before you’re sure — Status = “Review” or “Trial” until confirmed.

7. Scoring Prompt (starting draft)

“You will receive today’s content items (title, source, snippet), each tagged with the owner’s personal rating (H/M/L) and stated reasoning (‘Why’) for that source, where available. Score each item 0–100 for today’s digest.

Treat the owner’s Value+Why as the primary signal — e.g. a High-rated source with reasoning ‘deep technical analysis’ should score well if today’s item matches that description, and poorly if it’s an off-brand outlier (e.g. clickbait from a usually-good source). A Low-rated or unrated source can still score well if the specific item is unusually substantive.

Penalize generically: clickbait phrasing, vague superlatives, pure reaction/outrage content.

Return ONLY valid JSON: array of {id, score, reason}. Reason should reference the owner’s stated Why where relevant.”

(Expect 1-2 rounds of tuning once you see real output.)

8. Definition of Done (MVP)

  • Google Sheet extended to cover blogs/newsletters (not just YouTube)
  • Google Sheets node reads sheet successfully in n8n
  • FreshRSS API tested via manual HTTP call
  • Workflow A built per Section 5
  • Digest received via Telegram and written to Obsidian for 3 consecutive days
  • Error branches tested (FreshRSS down, Sheets down, bad AI response)
  • No manual FreshRSS browsing during 3-day test window

9. Phase 2 — Workflow B: Retrospective & Reconciliation

Trigger: Manual or weekly schedule, run once Workflow A has ~2 weeks of output.

Purpose: two distinct jobs bundled into one retrospective pass:

  1. Sheet accuracy check — are High-rated sources actually making the top 5 regularly? Are Low-rated sources sneaking in often (sheet may be stale)? Surface these as a summary for you to manually adjust ratings.
  2. New source reconciliation — you’ll be adding channels/blogs from Substack/YouTube exploration outside the sheet-first workflow (e.g. subscribing directly in FreshRSS or Substack before rating it). This workflow’s job: diff FreshRSS’s actual subscription list against the Google Sheet, and surface:
    • Sources in FreshRSS but not in the sheet → flag as “Unrated, needs review” (these were already scoring as “Unrated” in daily digests, but this surfaces them explicitly so they don’t stay invisible)
    • Sources in the sheet marked “Drop” but still active in FreshRSS → flag for cleanup
    • (Optional later) Sources getting picked in top-5 disproportionately vs. their rating, or never picked despite High rating

Rough shape (not built yet):

[Schedule Trigger] — weekly
        ↓
[HTTP Request] — FreshRSS: list all subscriptions
        ↓
[Google Sheets] — read subscriptions sheet
        ↓
[Code] — diff the two lists (name matching, same normalization as Workflow A)
        ↓
[Code] — (optional) pull last 2 weeks of digest history from Obsidian notes, tally which sources scored well
        ↓
[Telegram or Obsidian] — send/write reconciliation report: "3 new unrated sources found, 1 dropped source still active, X source over/under-performing vs rating"

This is deliberately not spec’d in detail yet — it depends on real Workflow A output existing first, and on you deciding how much of the reconciliation you want automated vs. just a prompt for manual review. Revisit after 2 weeks of MVP running.

10. Success Metric

You stop feeling the “pages and pages” dread, and your Google Sheet stays a living, trustworthy artifact — not another neglected list — because the retrospective workflow keeps it honest without demanding constant manual upkeep.