From 0f8b1ec4eb0d6bc87bba75b2e94b0130a333ae5f Mon Sep 17 00:00:00 2001 From: kenneth Date: Sun, 1 Mar 2026 22:41:19 +0000 Subject: [PATCH] fix: clamp boost values to [-1, 1] Additive layers can exceed the documented range. Co-authored-by: Ona --- .../src/time-of-day-enhancer.test.ts | 19 ++++++++++++++++--- .../src/time-of-day-enhancer.ts | 5 +++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/aris-feed-enhancers/src/time-of-day-enhancer.test.ts b/packages/aris-feed-enhancers/src/time-of-day-enhancer.test.ts index 3b744c3..c45b160 100644 --- a/packages/aris-feed-enhancers/src/time-of-day-enhancer.test.ts +++ b/packages/aris-feed-enhancers/src/time-of-day-enhancer.test.ts @@ -1,6 +1,6 @@ -import type { Context, FeedItem, FeedItemSignals } from "@aris/core" +import type { FeedItem, FeedItemSignals } from "@aris/core" -import { TimeRelevance } from "@aris/core" +import { Context, TimeRelevance } from "@aris/core" import { CalDavFeedItemType } from "@aris/source-caldav" import { CalendarFeedItemType } from "@aris/source-google-calendar" import { TflFeedItemType } from "@aris/source-tfl" @@ -20,7 +20,7 @@ import { // ============================================================================= function makeContext(date: Date): Context { - return { time: date } + return new Context(date) } function makeDate(year: number, month: number, day: number, hour: number, minute = 0): Date { @@ -673,6 +673,19 @@ describe("edge cases", () => { expect(result.boost!["w-current"]).toBeGreaterThan(0) }) + test("boost values are clamped to [-1, 1]", async () => { + // Morning weekday: TfL alert gets +0.6 from period rules. + // Pre-meeting adds +0.5. Total would be +1.1 without clamping. + const now = tuesday(8, 45) + const enhancer = createTimeOfDayEnhancer({ clock: () => now }) + const meeting = calendarEvent("c1", tuesday(9)) + const alert = tflAlert("tfl-1", 0.8) + const result = await enhancer([meeting, alert], makeContext(now)) + + expect(result.boost!["tfl-1"]).toBeLessThanOrEqual(1) + expect(result.boost!["tfl-1"]).toBeGreaterThanOrEqual(-1) + }) + test("suppress list is deduplicated", async () => { // An item that would be suppressed by both evening rules and pre-meeting low-urgency const now = tuesday(19, 45) diff --git a/packages/aris-feed-enhancers/src/time-of-day-enhancer.ts b/packages/aris-feed-enhancers/src/time-of-day-enhancer.ts index 2d2a76b..33924fc 100644 --- a/packages/aris-feed-enhancers/src/time-of-day-enhancer.ts +++ b/packages/aris-feed-enhancers/src/time-of-day-enhancer.ts @@ -123,6 +123,11 @@ export function createTimeOfDayEnhancer(options?: TimeOfDayEnhancerOptions): Fee const eveningLocation = hasEveningCalendarEventWithLocation(items, now) applyWeatherTimeCorrelation(items, period, dayType, eveningLocation, boost) + // Clamp boost values to [-1, 1] — additive layers can exceed the range + for (const id in boost) { + boost[id] = Math.max(-1, Math.min(1, boost[id]!)) + } + const result: FeedEnhancement = {} if (Object.keys(boost).length > 0) { result.boost = boost