Sift

A weekly dispatch of AI signal — curated for one reader

How Sift works

Sift turns a firehose of AI feeds into a two-minute weekly read. The whole design follows one rule: do everything possible locally and for free, and spend exactly one Claude API call per week — on the one step that actually needs judgment.

The pipeline

1 · Fetch

Pull every configured RSS/Atom feed over HTTP. A dead or slow feed is logged and skipped — it never kills the run.

fetch.py

2 · Filter

Drop anything already seen (tracked in SQLite) or older than eight days. Most of the firehose disappears here, for free.

cli.py · store.py

3 · Dedup

Cluster near-identical headlines with local string similarity, so the same story from five feeds collapses into one cluster.

dedup.py

5 · Weight & cut

Apply per-feed trust weights and drop anything below your min_score, then keep the top N.

filters.py

6 · Render

Write a self-contained HTML digest (and JSON) into the site's archive. Self-contained so it also works in email and offline.

render.py → docs/digests/

7 · Record, deliver, publish

Record the run and its exact cost, optionally email the digest, and rebuild this site so the new week shows in the archive.

store.py · deliver.py · site.py

Everything except step 4 is local and free.

Architecture

Sift is a handful of small, single-purpose Python modules, each independently testable. Business logic is separated from the I/O boundaries (network, the API call, SMTP, the filesystem) so the logic is unit-tested and the boundaries are mocked.

Module Responsibility
config.py Load + validate config.toml into immutable dataclasses (feeds, weights, mute, min_score, email)
fetch.py Fetch feeds, normalize entries to a common Item; a dead feed is logged, not fatal
dedup.py Greedy title-similarity clustering of near-duplicate items
rank.py The single Claude call: merge / categorize / score / summarize, as validated JSON
filters.py Mechanical post-rank source weighting and the min-score cutoff
render.py Build the digest and render self-contained HTML + JSON
site.py Generate the static site (explainer, guide, roadmap, archive)
deliver.py Email the digest over SMTP, password from the keychain
cost.py Per-model price table → the USD cost of a run
store.py SQLite history: seen URLs, and one row per run with tokens + cost
cli.py Wire it together; the run / add / list / email / history / site commands

What persists

Secrets live only in the macOS keychain (or env vars) — never in the repo.

Design principles

Cost

A typical week is a few thousand input tokens and a couple thousand output — cents per run on Opus, less on Sonnet. sift history shows the exact running total. See the features page for the full capability list and the guide to set it up.