@@ -230,11 +230,11 @@ func (app *App) fetchPRsInternal(ctx context.Context, waitForTurn bool) (incomin
230230 }
231231
232232 log .Printf ("[GITHUB] Found %d incoming, %d outgoing PRs from GitHub" , len (incoming ), len (outgoing ))
233- for _ , pr := range incoming {
234- log .Printf ("[GITHUB] Incoming PR: %s" , pr .URL )
233+ for i := range incoming {
234+ log .Printf ("[GITHUB] Incoming PR: %s" , incoming [ i ] .URL )
235235 }
236- for _ , pr := range outgoing {
237- log .Printf ("[GITHUB] Outgoing PR: %s" , pr .URL )
236+ for i := range outgoing {
237+ log .Printf ("[GITHUB] Outgoing PR: %s" , outgoing [ i ] .URL )
238238 }
239239
240240 // Fetch Turn API data
@@ -255,41 +255,63 @@ func (app *App) fetchPRsInternal(ctx context.Context, waitForTurn bool) (incomin
255255}
256256
257257// updatePRData updates PR data with Turn API results.
258- func (app * App ) updatePRData (url string , needsReview bool , isOwner bool , actionReason string ) * PR {
258+ func (app * App ) updatePRData (url string , needsReview bool , isOwner bool , actionReason string ) ( * PR , bool ) {
259259 app .mu .Lock ()
260260 defer app .mu .Unlock ()
261261
262262 if isOwner {
263263 // Update outgoing PRs
264264 for i := range app .outgoing {
265- if app .outgoing [i ].URL == url {
266- app .outgoing [i ].NeedsReview = needsReview
267- app .outgoing [i ].IsBlocked = needsReview
268- app .outgoing [i ].ActionReason = actionReason
269- return & app .outgoing [i ]
265+ if app .outgoing [i ].URL != url {
266+ continue
270267 }
268+ // Check if Turn data was already applied for this UpdatedAt
269+ now := time .Now ()
270+ if app .outgoing [i ].TurnDataAppliedAt .After (app .outgoing [i ].UpdatedAt ) {
271+ // Turn data already applied for this PR version, no change
272+ return & app .outgoing [i ], false
273+ }
274+ changed := app .outgoing [i ].NeedsReview != needsReview ||
275+ app .outgoing [i ].IsBlocked != needsReview ||
276+ app .outgoing [i ].ActionReason != actionReason
277+ app .outgoing [i ].NeedsReview = needsReview
278+ app .outgoing [i ].IsBlocked = needsReview
279+ app .outgoing [i ].ActionReason = actionReason
280+ app .outgoing [i ].TurnDataAppliedAt = now
281+ return & app .outgoing [i ], changed
271282 }
272283 } else {
273284 // Update incoming PRs
274285 for i := range app .incoming {
275- if app .incoming [i ].URL == url {
276- app .incoming [i ].NeedsReview = needsReview
277- app .incoming [i ].ActionReason = actionReason
278- return & app .incoming [i ]
286+ if app .incoming [i ].URL != url {
287+ continue
288+ }
289+ // Check if Turn data was already applied for this UpdatedAt
290+ now := time .Now ()
291+ if app .incoming [i ].TurnDataAppliedAt .After (app .incoming [i ].UpdatedAt ) {
292+ // Turn data already applied for this PR version, no change
293+ return & app .incoming [i ], false
279294 }
295+ changed := app .incoming [i ].NeedsReview != needsReview ||
296+ app .incoming [i ].ActionReason != actionReason
297+ app .incoming [i ].NeedsReview = needsReview
298+ app .incoming [i ].ActionReason = actionReason
299+ app .incoming [i ].TurnDataAppliedAt = now
300+ return & app .incoming [i ], changed
280301 }
281302 }
282- return nil
303+ return nil , false
283304}
284305
285306// fetchTurnDataSync fetches Turn API data synchronously and updates PRs directly.
286307func (app * App ) fetchTurnDataSync (ctx context.Context , issues []* github.Issue , user string , incoming * []PR , outgoing * []PR ) {
287308 turnStart := time .Now ()
288309 type prResult struct {
289- err error
290- turnData * turn.CheckResponse
291- url string
292- isOwner bool
310+ err error
311+ turnData * turn.CheckResponse
312+ url string
313+ isOwner bool
314+ wasFromCache bool
293315 }
294316
295317 // Create a channel for results
@@ -312,13 +334,14 @@ func (app *App) fetchTurnDataSync(ctx context.Context, issues []*github.Issue, u
312334 updatedAt := issue .GetUpdatedAt ().Time
313335
314336 // Call turnData - it now has proper exponential backoff with jitter
315- turnData , err := app .turnData (ctx , url , updatedAt )
337+ turnData , wasFromCache , err := app .turnData (ctx , url , updatedAt )
316338
317339 results <- prResult {
318- url : issue .GetHTMLURL (),
319- turnData : turnData ,
320- err : err ,
321- isOwner : issue .GetUser ().GetLogin () == user ,
340+ url : issue .GetHTMLURL (),
341+ turnData : turnData ,
342+ err : err ,
343+ isOwner : issue .GetUser ().GetLogin () == user ,
344+ wasFromCache : wasFromCache ,
322345 }
323346 }(issue )
324347 }
@@ -381,10 +404,11 @@ func (app *App) fetchTurnDataAsync(ctx context.Context, issues []*github.Issue,
381404
382405 turnStart := time .Now ()
383406 type prResult struct {
384- err error
385- turnData * turn.CheckResponse
386- url string
387- isOwner bool
407+ err error
408+ turnData * turn.CheckResponse
409+ url string
410+ isOwner bool
411+ wasFromCache bool
388412 }
389413
390414 // Create a channel for results
@@ -407,13 +431,14 @@ func (app *App) fetchTurnDataAsync(ctx context.Context, issues []*github.Issue,
407431 updatedAt := issue .GetUpdatedAt ().Time
408432
409433 // Call turnData - it now has proper exponential backoff with jitter
410- turnData , err := app .turnData (ctx , url , updatedAt )
434+ turnData , wasFromCache , err := app .turnData (ctx , url , updatedAt )
411435
412436 results <- prResult {
413- url : issue .GetHTMLURL (),
414- turnData : turnData ,
415- err : err ,
416- isOwner : issue .GetUser ().GetLogin () == user ,
437+ url : issue .GetHTMLURL (),
438+ turnData : turnData ,
439+ err : err ,
440+ isOwner : issue .GetUser ().GetLogin () == user ,
441+ wasFromCache : wasFromCache ,
417442 }
418443 }(issue )
419444 }
@@ -442,7 +467,8 @@ func (app *App) fetchTurnDataAsync(ctx context.Context, issues []*github.Issue,
442467 needsReview = true
443468 actionReason = action .Reason
444469 log .Printf ("[TURN] UnblockAction for %s: Reason=%q, Kind=%q" , result .url , action .Reason , action .Kind )
445- } else {
470+ } else if ! result .wasFromCache {
471+ // Only log "no action" for fresh API results, not cached ones
446472 log .Printf ("[TURN] No UnblockAction found for user %s on %s" , user , result .url )
447473 }
448474
@@ -452,14 +478,22 @@ func (app *App) fetchTurnDataAsync(ctx context.Context, issues []*github.Issue,
452478 NeedsReview : needsReview ,
453479 IsOwner : result .isOwner ,
454480 ActionReason : actionReason ,
481+ WasFromCache : result .wasFromCache ,
455482 }
456483
457484 app .mu .Lock ()
458485 app .pendingTurnResults = append (app .pendingTurnResults , turnResult )
459486 app .mu .Unlock ()
460487
461488 updatesApplied ++
462- log .Printf ("[TURN] Turn data received for %s (needsReview=%v, actionReason=%q) - buffered" , result .url , needsReview , actionReason )
489+ // Reduce verbosity - only log if not from cache or if blocked
490+ if ! result .wasFromCache || needsReview {
491+ cacheStatus := "fresh"
492+ if result .wasFromCache {
493+ cacheStatus = "cached"
494+ }
495+ log .Printf ("[TURN] %s data for %s (needsReview=%v, actionReason=%q)" , cacheStatus , result .url , needsReview , actionReason )
496+ }
463497 } else if result .err != nil {
464498 turnFailures ++
465499 }
@@ -475,15 +509,36 @@ func (app *App) fetchTurnDataAsync(ctx context.Context, issues []*github.Issue,
475509 app .loadingTurnData = false
476510 app .mu .Unlock ()
477511
478- log .Printf ("[TURN] Applying %d buffered Turn results" , len (pendingResults ))
512+ // Check if any results came from fresh API calls (not cache)
513+ var cacheHits , freshResults int
479514 for _ , result := range pendingResults {
480- app .updatePRData (result .URL , result .NeedsReview , result .IsOwner , result .ActionReason )
515+ if result .WasFromCache {
516+ cacheHits ++
517+ } else {
518+ freshResults ++
519+ }
520+ }
521+
522+ log .Printf ("[TURN] Applying %d buffered Turn results (%d from cache, %d fresh)" , len (pendingResults ), cacheHits , freshResults )
523+
524+ // Track how many PRs actually changed
525+ var actualChanges int
526+ for _ , result := range pendingResults {
527+ _ , changed := app .updatePRData (result .URL , result .NeedsReview , result .IsOwner , result .ActionReason )
528+ if changed {
529+ actualChanges ++
530+ }
481531 }
482532
483533 // Update tray title and menu with final Turn data if menu is already initialized
484534 app .setTrayTitle ()
485535 if app .menuInitialized {
486- log .Print ("[TURN] Turn data loaded, checking if menu needs update" )
487- app .updateMenuIfChanged (ctx )
536+ // Only trigger menu update if PR data actually changed
537+ if actualChanges > 0 {
538+ log .Printf ("[TURN] Turn data applied - %d PRs actually changed, checking if menu needs update" , actualChanges )
539+ app .updateMenuIfChanged (ctx )
540+ } else {
541+ log .Print ("[TURN] Turn data applied - no PR changes detected (cached data unchanged), skipping menu update" )
542+ }
488543 }
489544}
0 commit comments