From 896e2e3c998ebadceee205ec9373f83461e5066f Mon Sep 17 00:00:00 2001 From: 8tp <8tp@users.noreply.github.com> Date: Tue, 23 Jun 2026 10:18:59 -0500 Subject: [PATCH] fix(insights): device-coverage advisory follows YOUR enabled devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "Running on 2/3 devices ยท Missing: whoop, oura" card hardcoded a 3-device expectation (whoop/oura/apple) from before the Fitbit pivot, so it always flagged dormant WHOOP/Oura as missing. Base the advisory on the wearables actually in use โ€” enabled AND configured (has credentials) โ€” and only show it when you run 2+ devices and one of yours is absent today. A device you don't use no longer reads as "missing". Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/api/src/services/insights.ts | 33 ++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/apps/api/src/services/insights.ts b/apps/api/src/services/insights.ts index 2f7df48..87e220f 100644 --- a/apps/api/src/services/insights.ts +++ b/apps/api/src/services/insights.ts @@ -1,6 +1,7 @@ import type { Database } from 'better-sqlite3'; -import type { NormalizedDailySummary, InsightItem } from '@vcc/shared'; -import { ALERT_THRESHOLDS } from '@vcc/shared'; +import type { NormalizedDailySummary, InsightItem, DeviceSource } from '@vcc/shared'; +import { ALERT_THRESHOLDS, DEVICE_LABEL } from '@vcc/shared'; +import { computeIntegrationStatuses } from '../lib/integrationStatus.js'; /** * Rule-based insights (fast, deterministic, no LLM). These sit alongside the @@ -74,7 +75,13 @@ export function buildInsightsForDate(db: Database, today: NormalizedDailySummary }); } - // Device coverage advisory + // Device coverage advisory โ€” relative to the wearables the user actually runs + // (enabled AND configured). A device you don't use (dormant WHOOP/Oura with no + // credentials) is not "missing"; it simply isn't part of your setup. + const expected = computeIntegrationStatuses(db) + .filter((s) => s.kind === 'wearable' && s.enabled && s.configured) + .map((s) => s.id as DeviceSource); + if (today.devices.active === 0) { out.push({ id: 'no-devices', @@ -83,14 +90,18 @@ export function buildInsightsForDate(db: Database, today: NormalizedDailySummary body: 'Falling back to habit tracker + previous trends. Not an anomaly.', sources: [], }); - } else if (today.devices.active < 3) { - out.push({ - id: 'partial-coverage', - severity: 'blue', - title: `Running on ${today.devices.active}/3 devices`, - body: `Missing: ${['whoop', 'oura', 'apple'].filter((d) => !today.devices[d as 'whoop' | 'oura' | 'apple']).join(', ')}.`, - sources: [], - }); + } else { + const missing = expected.filter((d) => !today.devices[d]); + // Only flag when you run 2+ devices and one of YOURS is absent today. + if (expected.length >= 2 && missing.length > 0) { + out.push({ + id: 'partial-coverage', + severity: 'blue', + title: `Running on ${expected.length - missing.length}/${expected.length} devices`, + body: `Missing today: ${missing.map((d) => DEVICE_LABEL[d]).join(', ')}.`, + sources: [], + }); + } } return out;