From 70ca5a0d3a200b4049a554907ab63a48f1246fc9 Mon Sep 17 00:00:00 2001 From: Robin Kutesa Date: Sat, 20 Jun 2026 14:14:45 +0300 Subject: [PATCH] Cache backend tags so offline pill filtering stays accurate Add tags[] to CachedStory (defaulted for lightweight migration) and persist them on upsert; offline StorySummary now carries the cached tags instead of falling back to topic. Backend stays the source of truth. Co-Authored-By: Claude Opus 4.8 --- Jarvis/Models/Models.swift | 4 ++++ Jarvis/Views/Home/SignalFeedView.swift | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Jarvis/Models/Models.swift b/Jarvis/Models/Models.swift index aa6fc2f..1b1a64b 100644 --- a/Jarvis/Models/Models.swift +++ b/Jarvis/Models/Models.swift @@ -170,6 +170,9 @@ final class CachedStory { var headline: String var summary: String var topic: String + /// Canonical backend tags, so offline pill filtering stays accurate. + /// Defaulted for lightweight migration of existing cached rows. + var tags: [String] = [] var signalScore: Int var sourceCount: Int var consensus: String? @@ -182,6 +185,7 @@ final class CachedStory { self.headline = summary.headline self.summary = summary.summary self.topic = summary.topic + self.tags = summary.tags ?? [] self.signalScore = summary.signalScore self.sourceCount = summary.sourceCount self.consensus = summary.consensus diff --git a/Jarvis/Views/Home/SignalFeedView.swift b/Jarvis/Views/Home/SignalFeedView.swift index 68a639e..659de7d 100644 --- a/Jarvis/Views/Home/SignalFeedView.swift +++ b/Jarvis/Views/Home/SignalFeedView.swift @@ -466,6 +466,7 @@ struct SignalFeedView: View { if let found = existing?.first { found.signalScore = summary.signalScore found.sourceCount = summary.sourceCount + found.tags = summary.tags ?? [] found.updatedAt = summary.updatedAt } else { modelContext.insert(CachedStory(from: summary)) @@ -483,7 +484,7 @@ extension StorySummary { headline: cached.headline, summary: cached.summary, topic: cached.topic, - tags: nil, + tags: cached.tags, signalScore: cached.signalScore, scoreBreakdown: ScoreBreakdown(sourceAuthority: 0, freshness: 0, localRelevance: 0, crossSourceConfirmation: 0, topicImportance: 0),