krz/octosentry

macOS menu bar app to monitor GitHub security alerts

clone: git clone https://gitbay.org/krz/octosentry.git

88899df0f707d7140e72b43fefc47f97f1c35884

signed_unknown_key

author: Christian Cleberg <hello@cleberg.net> · 2026-08-22T23:02:04Z
committer: <noreply@github.com>

Start new installs with an empty watch list (#45)

The placeholder seeded every fresh install with ccleberg/cleberg.net,
so a new user's first fetch was someone else's repo, usually failing.
The empty state now points at the gear menu instead of claiming there
are no open alerts.

Refresh no longer treats an empty watch list as a complete poll. It
carries no information, and counting it as complete would prune every
dismissal and record every tracked alert as resolved — reachable before
by removing all repos, and the default path now.
 octosentry/PersistedState.swift           | 5 ++++-
 octosentry/SecurityEventListView.swift    | 8 ++++++++
 octosentry/SecurityEventStore.swift       | 6 +++++-
 octosentryTests/PersistedStateTests.swift | 5 +++++
 4 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/octosentry/PersistedState.swift b/octosentry/PersistedState.swift
index 9bc8b0a..b7147c4 100644
--- a/octosentry/PersistedState.swift
+++ b/octosentry/PersistedState.swift
@@ -93,8 +93,11 @@ nonisolated struct PersistedState: Codable {
         history = try container.decodeIfPresent(AlertHistory.self, forKey: .history) ?? AlertHistory()
     }
 
+    /// A fresh install watches nothing until the user adds a repo. Seeding a
+    /// specific repo here meant every new install started by fetching someone
+    /// else's alerts, which it usually can't read.
     static let placeholder = PersistedState(
-        watchedRepos: [WatchedRepo(fullName: "ccleberg/cleberg.net", accountID: 0)],
+        watchedRepos: [],
         seenEventIDs: [],
         lastFetchByRepo: [:],
         minimumSeverity: .low
diff --git a/octosentry/SecurityEventListView.swift b/octosentry/SecurityEventListView.swift
index 3ace252..21e13a2 100644
--- a/octosentry/SecurityEventListView.swift
+++ b/octosentry/SecurityEventListView.swift
@@ -277,6 +277,14 @@ struct SecurityEventListView: View {
                         tint: .secondary,
                         message: "\(store.totalFetchedCount) alert(s) are below your minimum severity filter"
                     )
+                } else if store.watchedRepos.isEmpty {
+                    // "No open alerts" would be misleading when nothing is
+                    // being watched in the first place.
+                    StatusView(
+                        systemImage: "plus.circle",
+                        tint: .secondary,
+                        message: "No repositories watched yet — add one from the gear menu"
+                    )
                 } else {
                     StatusView(systemImage: "checkmark.shield", tint: .green, message: "No open security alerts")
                 }
diff --git a/octosentry/SecurityEventStore.swift b/octosentry/SecurityEventStore.swift
index f27f621..e2f5db4 100644
--- a/octosentry/SecurityEventStore.swift
+++ b/octosentry/SecurityEventStore.swift
@@ -172,7 +172,11 @@ final class SecurityEventStore {
 
         // Only prune against a complete picture: if a repo failed this round
         // its alerts are missing, and pruning would forget they were hidden.
-        if fetchedEventsByRepo.count == Set(state.watchedRepos.map(\.fullName)).count {
+        // An empty watch list is not a complete picture either — it carries no
+        // information, and treating it as one would drop every dismissal and
+        // record every tracked alert as resolved.
+        let watchedRepoNames = Set(state.watchedRepos.map(\.fullName))
+        if !watchedRepoNames.isEmpty, fetchedEventsByRepo.count == watchedRepoNames.count {
             let now = Date()
             state.triage = state.triage.pruned(
                 presentEventIDs: Set(fetchedEvents.map(\.id)),
diff --git a/octosentryTests/PersistedStateTests.swift b/octosentryTests/PersistedStateTests.swift
index 874ad92..e97799b 100644
--- a/octosentryTests/PersistedStateTests.swift
+++ b/octosentryTests/PersistedStateTests.swift
@@ -112,6 +112,11 @@ struct PersistedStateTests {
         }
     }
 
+    // A fresh install must not arrive watching somebody else's repo.
+    @Test func placeholderWatchesNothing() {
+        #expect(PersistedState.placeholder.watchedRepos.isEmpty)
+    }
+
     @Test func placeholderStartsWithNoSeenStateAndNoRepoScope() {
         #expect(PersistedState.placeholder.seenEventIDs.isEmpty)
         #expect(PersistedState.placeholder.lastFetchByRepo.isEmpty)