d05963670ee4c6b2b54366dc284d32e2b7896356
hiram
  Fri Aug 14 09:22:16 2026 -0700
candidate for patch fixing automatic redirect from GCA to GCF assemblies for track or assembly hubs refs #38082

diff --git src/hg/lib/hubConnect.c src/hg/lib/hubConnect.c
index a5874268b06..567bf75da9f 100644
--- src/hg/lib/hubConnect.c
+++ src/hg/lib/hubConnect.c
@@ -1126,93 +1126,130 @@
 static unsigned lookForUndecoratedDb(char *name)
 // Look for this undecorated in the attached assembly hubs
 {
 struct trackHubGenome *genome = trackHubGetGenomeUndecorated(name);
 
 if (genome == NULL)
     return FALSE;
 
 struct trackHub *trackHub = genome->trackHub;
 
 if ((trackHub != NULL) && (trackHub->hubStatus != NULL))
     return trackHub->hubStatus->id;
 return 0;
 }
 
+static unsigned lookForGenarkHub(struct sqlConnection *conn, struct cart *cart, char *genarkPrefix, char *name, boolean *added)
+// See if genark hosts an assembly hub for this accession; if so attach it
+// and return its hub id (0 if genark doesn't have it, or the attach failed).
+{
+char query[4096];
+char buffer[4096];
+sqlSafef(query, sizeof query, "select hubUrl from %s where gcAccession='%s'", genarkTableName(), name);
+if (sqlQuickQuery(conn, query, buffer, sizeof buffer))
+    {
+    char url[4096];
+    safef(url, sizeof url, "%s/%s", genarkPrefix, buffer);
+
+    struct hubConnectStatus *status = getAndSetHubStatus( cart, url, TRUE);
+
+    if (status)
+        {
+        *added = TRUE;
+        return status->id;
+        }
+    }
+return 0;
+}
+
 static boolean lookForLonelyHubs(struct cart *cart, struct hubConnectStatus  *hubList, char **newDatabase, char *genarkPrefix)
 // We go through the hubs and see if any of them reference an assembly
 // that is NOT currently loaded, but we know a URL to load it.
 {
 struct sqlConnection *conn = hConnectCentral();
 if (!sqlTableExists(conn, genarkTableName()))
     return FALSE;
 
 boolean added = FALSE;
 
 struct hubConnectStatus  *hub;
 for(hub = hubList; hub; hub = hub->next)
     {
     struct trackHub *tHub = hub->trackHub;
     if (tHub == NULL)
         continue;
 
     struct trackHubGenome *genomeList = tHub->genomeList, *genome;
 
     for(genome = genomeList; genome; genome = genome->next)
         {
         char *name = genome->name;
 
-        name = asmAliasFind(name);
-        if (!hDbIsActive(name) )
-            {
+        // a non-NULL twoBitPath means this genome carries its own sequence,
+        // i.e. it's a real assembly hub, not just a track hub naming an
+        // assembly it expects to find hosted elsewhere.  Its own declared
+        // GCA/GCF accession is what should be displayed -- don't redirect it
+        // to a different, merely "equivalent" hub via asmAlias.
+        if (genome->twoBitPath != NULL)
+            continue;
+
+        char *aliasName = asmAliasFind(name);
+
+        // if the assembly is already active under its alias (a classic
+        // database, or a hub already attached under that name), there's
+        // nothing more to do
+        if (hDbIsActive(aliasName))
+            continue;
+
         char buffer[4096];
         unsigned newId = 0;
 
-            // look with undecorated name for an attached assembly hub
+        // Prefer keeping the hub on the accession exactly as the hub author
+        // declared it: only fall back to an asmAlias-equivalent accession
+        // if the declared one isn't available anywhere -- not already
+        // attached, and genark doesn't host it either.
+        char *targetName = name;
         if (!(newId = lookForUndecoratedDb(name)))
-                {
-                // see if genark has this assembly
-                char query[4096];
-                sqlSafef(query, sizeof query, "select hubUrl from %s where gcAccession='%s'", genarkTableName(), name);
-                if (sqlQuickQuery(conn, query, buffer, sizeof buffer))
-                    {
-                    char url[4096];
-                    safef(url, sizeof url, "%s/%s", genarkPrefix, buffer);
+            newId = lookForGenarkHub(conn, cart, genarkPrefix, name, &added);
 
-                    struct hubConnectStatus *status = getAndSetHubStatus( cart, url, TRUE);
-
-                    if (status)
+        if (!newId && differentString(aliasName, name))
             {
-                        newId = status->id;
-                        added = TRUE;
-                        }
-                    }
+            targetName = aliasName;
+            if (!(newId = lookForUndecoratedDb(aliasName)))
+                newId = lookForGenarkHub(conn, cart, genarkPrefix, aliasName, &added);
             }
 
         // if we found an id, change some names to use it as a decoration
         if (newId)
             {
-                safef(buffer, sizeof buffer, "hub_%d_%s", newId, name);
+            safef(buffer, sizeof buffer, "hub_%d_%s", newId, targetName);
 
             genome->name = cloneString(buffer);
 
-                // if our new database is an undecorated db, decorate it
-                if (*newDatabase && sameString(*newDatabase, name))
+            // genome was registered in tHub->genomeHash under its original
+            // name when the hub was parsed; trackHubFindGenome() looks
+            // genomes up by name through that hash, so re-register it under
+            // its new name now that we've renamed it in place, the same way
+            // hubConnectStatusForIdExt() does for quickLifted hubs.
+            hashAdd(tHub->genomeHash, genome->name, genome);
+            hashAdd(tHub->genomeHash, hubConnectSkipHubPrefix(genome->name), genome);
+
+            // if our new database is an undecorated db (either as the hub
+            // author declared it, or its asmAlias-equivalent form), decorate it
+            if (*newDatabase && (sameString(*newDatabase, name) || sameString(*newDatabase, aliasName)))
                 *newDatabase = cloneString(buffer);
             }
-
-            }
         }
     }
 
 hDisconnectCentral(&conn);
 return added;
 }
 
 static char *getCuratedHubPrefix()
 /* figure out what sandbox we're in. */
 {
 char *curatedHubPrefix = cfgOption("curatedHubPrefix");
 if (isEmpty(curatedHubPrefix))
     curatedHubPrefix = "public";
 
 return curatedHubPrefix;