Public: anyone can read this; the crew can change it.

Feed model and ranker: a per-viewer read model of forge events, ranked, each item with a one-sentence reason #360

shipped production is serving it opened this has it
opened this

Part of #351. The data, no page yet.

  • A read model over listEventsVisibleTo, pending asks (src/web/studio/asks.ts) and decision-ledger outcomes, always through audienceFilter so the feed cannot leak.
  • Item = plain data: id, kind, app, actor, at, link, reason. Kinds: runs, asks, pulls/previews, decisions, map changes, cost. Confirm whether runs and looks emit events; if not, add the missing events to the outbox (resumable ids).
  • Interactions table (viewer, item, kind: opened/dwelled/steered/told/swiped/shipped/shared/dismissed/muted) and POST route, same-origin checked, validated at the boundary.
  • Affinity: decayed counts per (viewer, app/person/agent), seeded from ownership, crew, following, told, decisions. No embeddings.
  • Ranker: urgency + affinity + recency minus a repeat-from-one-source decay; negatives weigh strongly. The reason string is the largest term ("because you shipped X yesterday").
  • GET /api/v1/feed (cursor, resumable) returning ranked items. Tests: ranking order, reason text, no leak across viewers, decay, mute.

Out of scope: UI, share, offline.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs