From c28e3ecc4beb10d7fed4ba6e577dc0b3fa11f181 Mon Sep 17 00:00:00 2001 From: Kenneth Date: Sat, 10 Jan 2026 19:35:55 +0000 Subject: [PATCH] Move POI alerts to FYI --- .../Orchestrator/ContextOrchestrator.swift | 13 +++- .../iris/Views/OrchestratorView.swift | 70 ++++++++++++------- 2 files changed, 55 insertions(+), 28 deletions(-) diff --git a/IrisCompanion/iris/Orchestrator/ContextOrchestrator.swift b/IrisCompanion/iris/Orchestrator/ContextOrchestrator.swift index a16c9bb..ac26e79 100644 --- a/IrisCompanion/iris/Orchestrator/ContextOrchestrator.swift +++ b/IrisCompanion/iris/Orchestrator/ContextOrchestrator.swift @@ -327,6 +327,7 @@ final class ContextOrchestrator: NSObject, ObservableObject { if pois.isEmpty { logger.info("no points of interests found") } + // POIs are FYI-only; do not compete for the right-now winner. for poi in pois.prefix(2) { let subtitle = poiSubtitle(for: poi) let confidence = min(max(poi.confidence, 0.0), 1.0) @@ -344,7 +345,6 @@ final class ContextOrchestrator: NSObject, ObservableObject { actions: ["DISMISS"] ) poiItems.append(item) - rightNowCandidates.append(.init(item: item, confidence: confidence, isEligibleForRightNow: true)) } case .failure(let error): fetchFailed = true @@ -402,10 +402,17 @@ final class ContextOrchestrator: NSObject, ObservableObject { return } - let eligibleUnsuppressed = rightNowCandidates.filter { ranked in - !store.isSuppressed(id: ranked.item.id, type: ranked.item.type, now: nowEpoch) + let poiCandidateCount = rightNowCandidates.filter { $0.item.type == .poiNearby }.count + if poiCandidateCount > 0 { + logger.warning("dropping poi candidates from right-now ranking count=\(poiCandidateCount)") } + let eligibleUnsuppressed = rightNowCandidates + .filter { $0.item.type != .poiNearby } + .filter { ranked in + !store.isSuppressed(id: ranked.item.id, type: ranked.item.type, now: nowEpoch) + } + let winnerSelection = ranker.pickWinner(from: eligibleUnsuppressed, now: nowEpoch, context: userContext) let winnerItem = winnerSelection?.item ?? FeedEnvelope.allQuiet(now: nowEpoch).feed[0] diff --git a/IrisCompanion/iris/Views/OrchestratorView.swift b/IrisCompanion/iris/Views/OrchestratorView.swift index 9b41ba2..7120f4e 100644 --- a/IrisCompanion/iris/Views/OrchestratorView.swift +++ b/IrisCompanion/iris/Views/OrchestratorView.swift @@ -45,7 +45,7 @@ struct OrchestratorView: View { Button("Recompute Now") { orchestrator.recomputeNow() } } - Section("Feed") { + Section("Winner") { if let feed = orchestrator.lastFeed, let winner = feed.winnerItem() { Text(winner.title) .font(.headline) @@ -54,36 +54,56 @@ struct OrchestratorView: View { .font(.subheadline) .foregroundStyle(.secondary) } - Text("type \(winner.type.rawValue) • prio \(String(format: "%.2f", winner.priority)) • ttl \(winner.ttlSec)s") - .font(.caption) - .foregroundStyle(.secondary) - - if feed.feed.count > 1 { - Divider() + LabeledContent("Type") { Text(winner.type.rawValue) } + LabeledContent("Bucket") { Text(winner.bucket.rawValue) } + LabeledContent("Priority") { Text(String(format: "%.2f", winner.priority)) } + LabeledContent("TTL") { Text("\(winner.ttlSec)s") } + if let poiType = winner.poiType { + LabeledContent("POI type") { Text(poiType.rawValue) } } + if let startsAt = winner.startsAt { + LabeledContent("Starts at") { Text("\(startsAt)") } + } + LabeledContent("ID") { + Text(winner.id) + .font(.caption) + .textSelection(.enabled) + } + } else { + Text("No winner yet") + .foregroundStyle(.secondary) + } + } - ForEach(feed.feed, id: \.id) { item in - VStack(alignment: .leading, spacing: 6) { - HStack { - Text(item.title) - .font(.headline) - .lineLimit(1) - Spacer() - Text(item.type.rawValue) + Section("Feed") { + if let feed = orchestrator.lastFeed { + if feed.feed.isEmpty { + Text("No feed items yet") + .foregroundStyle(.secondary) + } else { + ForEach(feed.feed, id: \.id) { item in + VStack(alignment: .leading, spacing: 6) { + HStack { + Text(item.title) + .font(.headline) + .lineLimit(1) + Spacer() + Text(item.type.rawValue) + .font(.caption) + .foregroundStyle(.secondary) + } + if !item.subtitle.isEmpty { + Text(item.subtitle) + .font(.subheadline) + .foregroundStyle(.secondary) + .lineLimit(1) + } + Text("bucket \(item.bucket.rawValue) • prio \(String(format: "%.2f", item.priority)) • ttl \(item.ttlSec)s") .font(.caption) .foregroundStyle(.secondary) } - if !item.subtitle.isEmpty { - Text(item.subtitle) - .font(.subheadline) - .foregroundStyle(.secondary) - .lineLimit(1) - } - Text("bucket \(item.bucket.rawValue) • prio \(String(format: "%.2f", item.priority)) • ttl \(item.ttlSec)s") - .font(.caption) - .foregroundStyle(.secondary) + .padding(.vertical, 4) } - .padding(.vertical, 4) } } else { Text("No feed yet")