* feat: add boost directive to FeedEnhancement
Post-processors can now return a boost map (item ID -> score)
to promote or demote items in the feed ordering. Scores from
multiple processors are summed and clamped to [-1, 1].
Co-authored-by: Ona <no-reply@ona.com>
* fix: correct misleading sort order comments
Co-authored-by: Ona <no-reply@ona.com>
---------
Co-authored-by: Ona <no-reply@ona.com>
Post-processors now receive Context as their 2nd parameter,
allowing them to use contextual data (time, location, etc.)
when producing enhancements.
Co-authored-by: Ona <no-reply@ona.com>
* feat: add post-processor pipeline to FeedEngine
Add FeedPostProcessor type and FeedEnhancement interface.
Post-processors run after item collection on all update
paths (refresh, reactive context, reactive items).
Pipeline is chained — each processor sees items as modified
by the previous one. Enhancement merging handles additional
items, suppression, and grouped items. Throwing processors
are caught and recorded in FeedResult.errors.
Co-authored-by: Ona <no-reply@ona.com>
* docs: document intentional TItems cast in post-processor merge
Co-authored-by: Ona <no-reply@ona.com>
* fix: filter stale item IDs from groups after pipeline
Groups accumulated during the pipeline can reference items
that a later processor suppressed. The engine now strips
stale IDs and drops empty groups before returning.
Co-authored-by: Ona <no-reply@ona.com>
* refactor: use reduce for stale group filtering
Co-authored-by: Ona <no-reply@ona.com>
---------
Co-authored-by: Ona <no-reply@ona.com>
* feat: replace FeedItem.priority with signals
Remove priority field from FeedItem and engine-level sorting.
Add FeedItemSignals with urgency and timeRelevance fields.
Update all source packages to emit signals instead of priority.
Ranking is now the post-processing layer's responsibility.
Urgency values are unchanged from the old priority values.
Co-authored-by: Ona <no-reply@ona.com>
* fix: use TimeRelevance enum in all tests
Co-authored-by: Ona <no-reply@ona.com>
---------
Co-authored-by: Ona <no-reply@ona.com>
Add lastFeed() method that returns cached FeedResult within
a configurable TTL (default 5 min). refresh() always fetches
fresh data and updates the cache. Periodic auto-refresh via
recursive setTimeout when engine is started. Reactive updates
reset the timer to avoid redundant fetches.
Co-authored-by: Ona <no-reply@ona.com>
Add listActions() and executeAction() to FeedSource for write
operations back to external services. Actions use arktype schemas
for input validation via StandardSchemaV1.
- ActionDefinition type with optional input schema
- FeedEngine routes actions with existence and ID validation
- Source IDs use reverse-domain format (aris.location, aris.tfl)
- LocationSource: update-location action with schema validation
- TflSource: set-lines-of-interest action with lineId validation
- No-op implementations for sources without actions
Co-authored-by: Ona <no-reply@ona.com>
Sources that cannot provide context now return null
instead of omitting the method. The engine checks the
return value rather than method existence.
Co-authored-by: Ona <no-reply@ona.com>
Introduces FeedEngine that consumes FeedSource instances and manages
the dependency graph for context flow and item collection.
- Validates dependency graph (missing deps, circular references)
- Topologically sorts sources for execution order
- Runs fetchContext() in dependency order, accumulating context
- Runs fetchItems() on all sources with final context
- Supports reactive updates via onContextUpdate/onItemsUpdate
- Graceful error handling (continues after source failures)
Marks DataSource, ContextProvider, ContextBridge, Reconciler, and
FeedController as deprecated in favor of FeedSource + FeedEngine.
Co-authored-by: Ona <no-reply@ona.com>