@@ -363,6 +363,46 @@ func (rt *RoutingTable) GetUnverifiedCount() int {
363363 return count
364364}
365365
366+ // PeerStateBreakdown provides a clear view of peer states
367+ type PeerStateBreakdown struct {
368+ Total int `json:"total"` // All known systems (not including self)
369+ Active int `json:"active"` // Verified, recent, responding (fail=0)
370+ Degraded int `json:"degraded"` // Verified but failing (0 < fail < max)
371+ Pending int `json:"pending"` // Heard via gossip, not yet verified
372+ Stale int `json:"stale"` // Was verified but outside cutoff window
373+ }
374+
375+ // GetPeerStateBreakdown returns counts of peers in each state
376+ func (rt * RoutingTable ) GetPeerStateBreakdown () PeerStateBreakdown {
377+ rt .cacheMu .RLock ()
378+ defer rt .cacheMu .RUnlock ()
379+
380+ now := time .Now ()
381+ cutoff := now .Add (- VerificationCutoff )
382+
383+ breakdown := PeerStateBreakdown {
384+ Total : len (rt .systemCache ),
385+ }
386+
387+ for _ , cached := range rt .systemCache {
388+ if ! cached .Verified {
389+ // Never verified - pending
390+ breakdown .Pending ++
391+ } else if cached .LastVerified .IsZero () || cached .LastVerified .Before (cutoff ) {
392+ // Verified but too long ago - stale
393+ breakdown .Stale ++
394+ } else if cached .FailCount > 0 {
395+ // Recently verified but now failing - degraded
396+ breakdown .Degraded ++
397+ } else {
398+ // Verified, recent, responding - active
399+ breakdown .Active ++
400+ }
401+ }
402+
403+ return breakdown
404+ }
405+
366406// GetCacheSize returns the number of cached systems
367407func (rt * RoutingTable ) GetCacheSize () int {
368408 rt .cacheMu .RLock ()
0 commit comments