package app import "strings" func isTransportClientOwnerLockActive(st TransportClientStatus) bool { switch st { case TransportClientUp, TransportClientStarting, TransportClientDegraded: return true default: return false } } func detectTransportOwnerLockConflicts(current, next []TransportPolicyIntent, clients []TransportClient) []TransportConflictRecord { currentOwners := map[string]string{} for _, raw := range current { norm, key, _, err := normalizeTransportIntent(raw) if err != nil || key == "" || strings.TrimSpace(norm.ClientID) == "" { continue } currentOwners[key] = norm.ClientID } if len(currentOwners) == 0 { return nil } activeByClientID := map[string]bool{} for _, client := range clients { id := strings.TrimSpace(client.ID) if id == "" { continue } activeByClientID[id] = isTransportClientOwnerLockActive(client.Status) } conflicts := make([]TransportConflictRecord, 0, 4) for _, raw := range next { norm, key, _, err := normalizeTransportIntent(raw) if err != nil || key == "" || strings.TrimSpace(norm.ClientID) == "" { continue } prevOwner, exists := currentOwners[key] if !exists || prevOwner == norm.ClientID { continue } if !activeByClientID[prevOwner] { continue } conflicts = append(conflicts, TransportConflictRecord{ Key: key, Type: "owner_lock", Severity: "block", Owners: []string{prevOwner, norm.ClientID}, Reason: "selector is locked to active owner; stop previous owner before switching", SuggestedResolution: "disconnect/stop current owner client, then validate/apply switch again", }) } return dedupeTransportConflicts(conflicts) }