bb8470a90643b057df7bbf5d0140a5b2bbf9dddd
braney
  Fri Jun 12 08:44:37 2026 -0700
quickLift: load genePreds by column name, not a fixed 15-col loader

Lifting a genePred track to another assembly used genePredExtLoad15, a
positional loader that assumes the extended genePred columns. Classic
knownGene-format tables instead carry proteinID and alignID after the ten
core columns, so the loader read proteinID as the integer score field and
aborted with "invalid signed integer" (e.g. "O54946", or "" when empty).
This showed up when lifting from an assembly whose knownGene is the legacy
format, such as mm10 to mm39 or rheMac10.

Add quickLiftGenePreds(), which loads through genePredReader so the actual
set of columns in the table is honored by name (proteinID maps to name2,
score defaults), matching how the tracks load when not lifted. The three
call sites that hardcoded genePredExtLoad15 (hgTracks gene loading and two
hgc detail handlers) now use it. The chain-walking shared with quickLiftSql
is factored into quickLiftLoadChains() and quickLiftChainQueryRange().

refs #37535

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

diff --git src/hg/hgTracks/simpleTracks.c src/hg/hgTracks/simpleTracks.c
index ffc44a57987..c2fc00ab771 100644
--- src/hg/hgTracks/simpleTracks.c
+++ src/hg/hgTracks/simpleTracks.c
@@ -6304,34 +6304,31 @@
 }
 
 static void maybeLiftGenePred(struct track *tg, char *table, char *chrom, int start, int end, boolean extra)
 /* Load a bunch of genePreds, perhaps quickLifting them. */
 {
 char *liftDb = cloneString(trackDbSetting(tg->tdb, "quickLiftDb"));
 
 if (liftDb != NULL)
     {
     char *table;
     quickLiftResolveTable(tg->tdb, tg->table, &table, &liftDb);
     struct hash *chainHash = newHash(8);
     struct sqlConnection *conn = hAllocConn(liftDb);
     char *quickLiftFile = cloneString(trackDbSetting(tg->tdb, "quickLiftUrl"));
 
-// using this loader on genePred tables with less than 15 fields may be a problem.
-extern struct genePred *genePredExtLoad15(char **row);
-
-    struct genePred *gpList = (struct genePred *)quickLiftSql(conn, quickLiftFile, table, chromName, winStart, winEnd,  NULL, NULL, (ItemLoader2)genePredExtLoad15, 0, chainHash);
+    struct genePred *gpList = quickLiftGenePreds(conn, quickLiftFile, table, chromName, winStart, winEnd, NULL, chainHash);
     hFreeConn(&conn);
 
     calcLiftOverGenePreds( gpList, chainHash, 0.0, 0.0, TRUE, NULL, NULL,  TRUE, FALSE);
     struct genePred *gp = gpList;
 
     struct linkedFeatures *lfList = NULL;
     for(;gp; gp = gp->next)
         {
         if (gp->chrom == NULL) // if the lift failed, ignore this one
             continue;
         if (positiveRangeIntersection(winStart, winEnd, gp->txStart, gp->txEnd) == 0)
             continue;
         slAddHead(&lfList, linkedFeaturesFromGenePred(tg, gp, TRUE));
         }
     slReverse(&lfList);