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 infeed_item_state(per raw item), mirroringfeed_state(per note) — the note pool's shown_count is untouched. - Mix: a feed draw of n reserves
n/4slots (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(fromraw_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).