Skip to content

Feed content-source routing (ARGUS-63)

Problem

The resurface feed (/ui/feed) draws only distilled notes. Saved content — Karakeep bookmarks today; podcasts/articles tomorrow — lands in raw_items (source=bookmark, kind=document), gets distilled into notes, and then the SAVED THING itself never resurfaces: you see a distilled fact from an article you never actually read. "Saved-but-unread" is exactly what a resurfacing feed exists for.

Routing semantics (v1)

  • Content lane predicate: raw_items.kind = 'document' AND source IN (feedContentSources). The list starts as {bookmark}; podcasts/articles join by extending one constant, no schema change.
  • Same Readwise decay model as notes: weight 2^-shown_count, weighted random draw without replacement, shown_count incremented on every surfacing. Decay IS the read semantics — there is deliberately no read/dismiss tracking in v1; an item the user keeps ignoring halves its own weight each time and rotates out of the way. Lane state lives in feed_item_state (per raw item), mirroring feed_state (per note) — the note pool's shown_count is untouched.
  • Mix: a feed draw of n reserves n/4 slots (integer floor, so 2 of the default 8) for the content lane. Fewer content items than slots: the remainder goes to notes. No content items: all-notes, byte-identical to the old behavior. Content cards draw FIRST, notes fill the rest — the lane can never crowd notes below 3/4 of the page.
  • Card shape: content cards carry kind ("bookmark"; notes are "note"), url (from raw_items.metadata->>'url'), and title = the body's first line (bookmarkToCapture writes the bookmark title there). The UI renders the title as a link.

Out of scope (v1)

Explicit read/dismiss state; per-source mix ratios; routing monitor episodes (operational output, not saved content — it does not belong in this lane).