Replace FeedItem.priority with signals (#39)

* 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>
This commit is contained in:
2026-02-28 12:02:57 +00:00
committed by GitHub
parent 78b0ed94bd
commit 28d26b3c87
17 changed files with 278 additions and 145 deletions

View File

@@ -1,6 +1,6 @@
import type { ActionDefinition, Context, FeedSource } from "@aris/core"
import type { ActionDefinition, Context, FeedItemSignals, FeedSource } from "@aris/core"
import { UnknownActionError, contextValue } from "@aris/core"
import { TimeRelevance, UnknownActionError, contextValue } from "@aris/core"
import { LocationKey } from "@aris/source-location"
import { type } from "arktype"
@@ -18,12 +18,18 @@ import { TflApi, lineId } from "./tfl-api.ts"
const setLinesInput = lineId.array()
const SEVERITY_PRIORITY: Record<TflAlertSeverity, number> = {
const SEVERITY_URGENCY: Record<TflAlertSeverity, number> = {
closure: 1.0,
"major-delays": 0.8,
"minor-delays": 0.6,
}
const SEVERITY_TIME_RELEVANCE: Record<TflAlertSeverity, TimeRelevance> = {
closure: TimeRelevance.Imminent,
"major-delays": TimeRelevance.Imminent,
"minor-delays": TimeRelevance.Upcoming,
}
/**
* A FeedSource that provides TfL (Transport for London) service alerts.
*
@@ -137,19 +143,26 @@ export class TflSource implements FeedSource<TflAlertFeedItem> {
closestStationDistance,
}
const signals: FeedItemSignals = {
urgency: SEVERITY_URGENCY[status.severity],
timeRelevance: SEVERITY_TIME_RELEVANCE[status.severity],
}
return {
id: `tfl-alert-${status.lineId}-${status.severity}`,
type: "tfl-alert",
priority: SEVERITY_PRIORITY[status.severity],
timestamp: context.time,
data,
signals,
}
})
// Sort by severity (desc), then by proximity (asc) if location available
// Sort by urgency (desc), then by proximity (asc) if location available
items.sort((a, b) => {
if (b.priority !== a.priority) {
return b.priority - a.priority
const aUrgency = a.signals?.urgency ?? 0
const bUrgency = b.signals?.urgency ?? 0
if (bUrgency !== aUrgency) {
return bUrgency - aUrgency
}
if (a.data.closestStationDistance !== null && b.data.closestStationDistance !== null) {
return a.data.closestStationDistance - b.data.closestStationDistance