02710f0107a6b154d9e5689165941ce724cb4c6a
braney
  Fri Sep 4 13:38:54 2026 -0700
quickLift: fix the seams a second review pass found

quickLiftPslBackToProtein left two things wrong.  pslTransMap can hand back
strand[0] == '-', because it reverse complements the input when the two
alignments disagree about the strand of the sequence they share, and forcing
strand[1] to '+' on top of that produced "-+".  A protein psl is only ever "++"
or "+-", and pslShow reads strand[0] == '-' as "reverse complement the query", so
it would have reverse complemented a protein as though it were DNA.  It now turns
the alignment over so the minus lands on the target side.  qBaseInsert is in
nucleotides like everything else being divided, so it comes down too, and it
joins the divisibility guard:  without it the result failed pslCheck and the
number was printed verbatim on the details page.

Adding that back-conversion made a comment in pslTrack.c false.  The lift no
longer always returns an untranslated alignment, so the drawing code has to ask
rather than assume, the way bigBedTrack.c already did.  A quickLifted protein psl
track was drawing every block at a third of its length.  No such track exists on
hg19 or hg38 today, so this was latent.

The normalized score on the chain details page was read from the assembly on
screen.  Where that assembly has no such table the page died; where it has a
table of the same name, which is the common case for a self or a well known
chain track, it silently returned some other assembly's chain and printed a blank
score.  It now reads the assembly the chain came from, on a connection to it.

Two smaller things: htcBigPslAli guarded its connection with trackHubDatabase
alone, but a GenArk accession does not start with hub_, so it matches the guard
genericClickHandlerPlus already uses; and the table name tests in cds.c now skip
the hub prefix the way the ones in hgc.c were changed to, so a lifted refSeqAli
reaches its special case.

The chain item label took its start from the source chain and its strand
character from the lifted one.  Both now come from the source chain.

refs #38249

diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c
index 4c8c4965512..d78eb59dd1f 100644
--- src/hg/hgc/hgc.c
+++ src/hg/hgc/hgc.c
@@ -4152,50 +4152,67 @@
     printf(" size: %d<BR>\n", chain->qEnd - chain->qStart);
     }
 printf("<B>Chain ID:</B> %s<BR>\n", item);
 printf("<B>Score:</B> %1.0f\n", chain->score);
 
 if (nullSubset)
     printf("<B>Score within browser window:</B> N/A (no aligned bases)<BR>\n");
 else if (otherIsActive && subChain != chain)
     printf("<B>&nbsp;&nbsp;Approximate Score within browser window:</B> %1.0f<BR>\n",
 	   subSetScore);
 else
     printf("<BR>\n");
 
 boolean normScoreAvailable = chainDbNormScoreAvailable(tdb);
 
+// The normalized score lives in the chain table, so for a quickLifted track it has to be
+// read from the assembly the chain came from.  Against the assembly on screen the table
+// name either does not resolve, or resolves to a same-named table there and returns
+// somebody else's chain, which is worse.
+char *normDb = database;
+struct sqlConnection *normConn = conn;
+char *normTable = tdb->table;
+boolean lifted = quickLiftIsLifted(tdb) && !quickLiftIsOwnChainTrack(tdb);
+if (lifted)
+    {
+    normDb = trackDbSetting(tdb, "quickLiftDb");
+    normTable = trackHubSkipHubName(tdb->table);
+    normConn = hAllocConn(normDb);
+    }
+
 if (normScoreAvailable)
     {
     char tableName[HDB_MAX_TABLE_STRING];
-    if (!hFindSplitTable(database, chain->tName, tdb->table, tableName, sizeof tableName, NULL))
-	errAbort("genericChainClick track %s not found", tdb->table);
+    if (!hFindSplitTable(normDb, chain->tName, normTable, tableName, sizeof tableName, NULL))
+	errAbort("genericChainClick track %s not found", normTable);
     char query[256];
     struct sqlResult *sr;
     char **row;
     sqlSafef(query, ArraySize(query),
 	 "select normScore from %s where id = '%s'", tableName, item);
-    sr = sqlGetResult(conn, query);
+    sr = sqlGetResult(normConn, query);
     if ((row = sqlNextRow(sr)) != NULL)
         {
         double normScore = atof(row[0]);
         int basesAligned = chain->score / normScore;
 	printf("<B>Normalized Score:</B> %1.0f (aligned bases: %d)", normScore, basesAligned);
         }
     sqlFreeResult(&sr);
     printf("<BR>\n");
     }
+if (lifted)
+    hFreeConn(&normConn);
 
 if (quickLiftIsLifted(tdb) && !quickLiftIsOwnChainTrack(tdb))
     // A lifted chain is only worked out over the window being viewed, so the whole chain's
     // extent is not knowable here and the usual sentence would be wrong.
     printf("<BR>This chain comes from %s and is mapped onto %s as the browser draws it, so "
            "the fields above describe the part of it around the window rather than the "
            "whole chain.<BR>\n",
            trackDbSetting(tdb, "quickLiftDb"), trackHubSkipHubName(database));
 else
     printf("<BR>Fields above refer to entire chain or gap, not just the part inside the window.<BR>\n");
 printf("<BR>\n");
 
 chainWinSize = min(winEnd-winStart, chain->tEnd - chain->tStart);
 /* Show alignment if the database exists and */
 /* if there is a chromInfo table for that database and the sequence */
@@ -8724,31 +8741,31 @@
 char *aliTable;
 int start;
 unsigned int cdsStart = 0, cdsEnd = 0;
 struct sqlConnection *conn = NULL;
 struct trackDb *tdb = NULL;
 
 aliTable = cartString(cart, "aliTable");
 // A quickLifted track's alignments live in the file of the assembly they came from, and
 // the position we were sent is on the reference.
 struct quickLiftAli ali;
 quickLiftAliInfo(aliTable, &ali);
 tdb = ali.tdb;
 if (tdb == NULL)
     errAbort("BUG: bigPsl alignment table '%s' not found; this maybe causes by `.' in track names", aliTable);
              
-if (!trackHubDatabase(ali.db))
+if (!trackHubDatabase(ali.db) && !isGenArk(ali.db))
     conn = hAllocConnTrack(ali.db, tdb);
 
 char title[1024];
 safef(title, sizeof title, "%s vs Genomic [%s]", acc, aliTable);
 alnModernStart(title);
 
 /* Get some environment vars. */
 start = cartInt(cart, "l");
 int end = cartInt(cart, "r");
 char *chrom = cartString(cart, "c");
 
 char *seq = NULL, *cdsString = NULL;
 struct lm *lm = lmInit(0);
 char *fileName = bbiNameFromSettingOrTable(tdb, conn, tdb->table);
 struct bbiFile *bbi =  bigBedFileOpenAlias(fileName, chromAliasFindAliases);