@@ -142,6 +142,35 @@ func ProgramWatchOnRefresh(programs []ProgramSummary) {
142142 _ = db .SetSetting (programWatchWatermarkKey , freshest .LatestTargetUpdatedAt )
143143}
144144
145+ // ProgramWatchCheckProgram fires the watch for a SINGLE program — used when a
146+ // force-refreshed scope summary comes back (e.g. user searched the Programs page
147+ // for "ryan-bbp"). If the program's latest_target_updated_at is newer than the
148+ // persisted watermark, alert immediately and advance the watermark so the next
149+ // warmer refresh doesn't re-alert. No-op when watch is disabled or the program
150+ // can't be checked. Bypasses the boot intro and the per-cycle cap.
151+ func ProgramWatchCheckProgram (p ProgramSummary ) {
152+ if ! programWatchEnabled () || ! programWatchPlatformAllowed (p .Platform ) {
153+ return
154+ }
155+ if p .LatestTargetUpdatedAt == "" {
156+ return
157+ }
158+ programWatchMu .Lock ()
159+ defer programWatchMu .Unlock ()
160+ watermark , _ := db .GetSetting (programWatchWatermarkKey )
161+ if watermark == "" {
162+ // No baseline yet — let the regular refresh path establish it silently
163+ // (otherwise the first search after a fresh install would alert on
164+ // whatever happened to be cached).
165+ return
166+ }
167+ if ! isNewerProgramTime (p .LatestTargetUpdatedAt , watermark ) {
168+ return
169+ }
170+ sendProgramUpdateDiscord (p )
171+ _ = db .SetSetting (programWatchWatermarkKey , p .LatestTargetUpdatedAt )
172+ }
173+
145174func sendProgramIntroDiscord (p ProgramSummary ) {
146175 msg := fmt .Sprintf ("📌 **Scope watch ready** — most recent update: **%s** (`%s` · %s)\n • Latest asset: `%s`\n • Updated: %s" ,
147176 programWatchName (p ), p .Handle , strings .ToUpper (p .Platform ),
0 commit comments