feat: user feeds — RSS sources, per-feed AI curation, telegram digests - #33
Merged
Conversation
All pre-commit checks run manually and green except check:tf, skipped with user approval (terraform provider downloads blocked in this env). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RWYc22xqR5mh5Qpd26hTm6
New feeds tab: create multiple feeds, each with its own title, AI-curation preferences, up to 10 RSS/Atom sources (parsed with feedsmith), three telegram daily slots (owner's timezone), a curated items page (newest 20 relevant of the last fetch), and an archive of every curation ever. Telegram delivery is send-once per item (feed_items.sentAt). HN homepage pipeline untouched; feeds get their own system prompt over the shared AI batching core. All pre-commit checks run manually and green (unit 143, e2e 32) except check:tf, skipped with user approval (provider downloads blocked here). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RWYc22xqR5mh5Qpd26hTm6
- Enforce the 5 MB cap while streaming the response body (readBodyCapped), not after buffering; keep the Content-Length fast path. Unit-tested with a chunked stream. - Cap keeps the 50 newest items (publishedAt-aware stable sort), not the first 50 in document order. - Archive e2e now actually exercises items falling out of the current fetch (source swap between runs) and asserts they stay archive-only. - Replace O(n²) toEvaluate.indexOf verdict lookup with a link-keyed map. Checks green (unit 146, e2e 32); check:tf skipped per user approval. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RWYc22xqR5mh5Qpd26hTm6
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RWYc22xqR5mh5Qpd26hTm6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the feeds feature: a new tab where each user creates any number of feeds, each with its own RSS/Atom sources, AI-curation preferences, and Telegram delivery slots. The HN homepage pipeline is untouched.
Spec:
docs/specs/feeds/index.md· SA validation: approved round 1 · Review: approved round 2 (docs/specs/feeds/review-*.md).What's in it
feeds,feed_sources(unique(feed_id, url)),feed_items— verdicts stored inline withpref_versionreuse mirroringcurations,current= member of the latest fetch, unique(feed_id, link)dedupe, andsent_atas the send-once Telegram guard.RssClientseam (worker/lib/rss.ts) parsing RSS 2.0 / Atom / RDF / JSON Feed via feedsmith behind a zod re-parse; drops titleless/non-http items, keeps the 50 newest per source, rejects bodies over 5 MB while streaming (user-supplied URLs). Fake client for hermetic e2e.select()stay byte-identical./api/feeds: CRUD, sources add/remove (validated by fetching the feed at add time), items (newest ≤20 relevant of the last fetch), archive (every curation ever), slots (409 until Telegram is linked), and per-feed rate-limitedrun(429 +Retry-AfterofflastFetchedAt).sent_atstamped on exactly the delivered items; overflow rolls to the next slot). Maintenance prunes only stale never-relevant items — the archive is kept forever.feedsnav tab; overview (cards with title + preferences, create form), feed page (feed-switcher dropdown, back link, Refresh, Archive, Settings, New feed), per-feed archive page, settings page (title, preferences, sources, slots via the sharedSlotTimesEditor, delete).ApiRequestErrornow carries the server's{ error }message.docs/0-backlog/newsletter-email-sources.md): newsletter-email addresses as feed sources — needs Cloudflare Email Routing infra; schema keeps it additive.Checks
check:tfskipped with user approval — terraform provider downloads are blocked in the CI-less dev container.🤖 Generated with Claude Code
https://claude.ai/code/session_01RWYc22xqR5mh5Qpd26hTm6
Generated by Claude Code