e214ffa17d2231d1a7413af72c539c1d3bfa0af2
braney
  Wed May 6 13:36:15 2026 -0700
hgConvert quickLift: still lift hidden composite siblings

c4e77d5 stopped validating hidden subtracks to keep them out of the
"failed to lift" warning, but that also dropped them from the generated
quickLift hub, so a composite came across with only its visible
subtracks. Validate every sibling again so all liftable ones make it
into the hub; pass a NULL badList for hidden siblings so non-liftable
ones still don't show up in the user-facing complaint.

Verified with refSeqComposite (default vis: only ncbiRefSeqCurated
visible): pre-fix hub contained only ncbiRefSeqCurated; post-fix hub
contains all 10 RefSeq subtracks with hidden ones marked
"parent refSeqComposite off". cons100way badList unchanged: hidden
phastCons100way (wig, non-liftable) is silenced on both.

refs #36125

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

diff --git src/hg/lib/trackHub.c src/hg/lib/trackHub.c
index ff3abae0a3f..b747cfc69e0 100644
--- src/hg/lib/trackHub.c
+++ src/hg/lib/trackHub.c
@@ -1726,44 +1726,46 @@
 
 if (tdbIsSuperTrack(tdb))
     {
     dyStringPrintf(dy, "superTrack on show\n");
     }
 
 if (!isVetted(track))
     dyStringPrintf(dy, "avoidHandler on\n");
     
 dumpTdbAndChildren(cart, dy, tdb);
 
 return dy;
 }
 
 static boolean validateOneTdb(char *db, struct trackDb *tdb, struct trackDb **badList)
-/* Make sure the tdb is a track type we grok. */
+/* Make sure the tdb is a track type we grok.  badList may be NULL to validate
+ * silently (no user-facing complaint about non-liftable types). */
 {
 if (sameString("cytoBandIdeo", trackHubSkipHubName(tdb->track)) ||
     !( startsWith("bigBed", tdb->type) || \
        startsWith("bigWig", tdb->type) || \
        startsWith("bigDbSnp", tdb->type) || \
        startsWith("bigGenePred", tdb->type) || \
        startsWith("gvf", tdb->type) || \
        startsWith("genePred", tdb->type) || \
        startsWith("narrowPeak", tdb->type) || \
        startsWith("bigLolly", tdb->type) || \
        sameString("bed", tdb->type) ||
        startsWith("bed ", tdb->type)))
     {
+    if (badList != NULL)
         slAddHead(badList, tdb);
     return FALSE;
     }
 
 // make sure we have a bigDataUrl
 if (startsWith("bigBed", tdb->type) || \
        startsWith("bigWig", tdb->type))
     {
     char *fileName = cloneString(trackDbSetting(tdb, "bigDataUrl"));
 
     if (fileName == NULL)
         {
         struct sqlConnection *conn = hAllocConnTrack(db, tdb);
         fileName = bbiNameFromSettingOrTable(tdb, conn, tdb->table);
         hashAdd(tdb->settingsHash, "bigDataUrl", fileName);
@@ -1790,35 +1792,37 @@
         view->subtracks = validateTdbChildren(cart, db,view->subtracks, badList);
 
         if (view->subtracks != NULL)
             {
             slAddHead(&validTdbs, view);
             if (view->visibility)
                 count++;
             }
         }
     }
 else
     {
     for(; tdb; tdb = nextTdb)
         {
         nextTdb = tdb->next;
-        if (!isParentVisible(cart, tdb) || !isSubtrackVisible(cart, tdb))
-            continue;
-        if (validateOneTdb(db, tdb, badList))
+        boolean visible = isParentVisible(cart, tdb) && isSubtrackVisible(cart, tdb);
+        // Lift all siblings of a visible subtrack, but only complain about
+        // non-liftable ones the user actually asked for (visible ones).
+        if (validateOneTdb(db, tdb, visible ? badList : NULL))
             {
             slAddHead(&validTdbs, tdb);
+            if (visible)
                 count++;
             }
         }
     }
 if (count)
     return validTdbs;
 
 return NULL;
 }
 
 static boolean validateTdb(struct cart *cart, char *db, struct trackDb *tdb, struct trackDb **badList)
 // make sure we only output track types that can
 // be quickLifted.  Return true if we any tracks survive
 {
 if (tdb->subtracks)