03bd4721a68ec74cab51164ff2d4b216cd6fe9b0
max
  Tue Sep 15 05:49:00 2026 -0700
trackDb README and comments: $hgsid is for native description pages only, refs #38353

The README still described $hgsid as a hub-page variable that is empty in native trackDb;
both halves of that are now the other way round.  The three call sites of
hVarSubstTrackDbHtml that still said a hub page is the only reason it exists say what the
call does now.

diff --git src/hg/hgTrackUi/hgTrackUi.c src/hg/hgTrackUi/hgTrackUi.c
index 10e04213782..27666198284 100644
--- src/hg/hgTrackUi/hgTrackUi.c
+++ src/hg/hgTrackUi/hgTrackUi.c
@@ -4712,32 +4712,33 @@
 if (tdb == NULL)
    {
    errAbort("Can't find %s in track database %s chromosome %s",
 	    track, database, chromosome);
    }
 
 // Do  little more dupe handling - make a tdb for dupe if any 
 if (isDup)
     {
     struct dupTrack *dup = dupTrackFindInList(dupList, dupWholeName);
     if (dup == NULL)
         errAbort("Can't find duplicate track %s", dupWholeName);
     tdb = dupTdbFrom(tdb, dup);
     }
 
-// A hub's description page never went through hgTrackDb, so its variables are substituted
-// here instead.  This is what lets a hub page link to its container with $parentTrack.
+// resolve $hgsid, which hgTrackDb had no cart to resolve, and for a hub the rest of its
+// description page variables: a hub page never went through hgTrackDb at all.  This is what
+// lets a hub page link to its container with $parentTrack.
 hVarSubstTrackDbHtml(cart, tdb, database);
 
 if(cartOptionalString(cart, "ajax"))
     {
     // html is going to be used w/n a dialog in hgTracks.js so serve up stripped down html
     // still need CSP2 header for security
     printf("%s", getCspMetaHeader());
     trackUi(tdb, tdbList, ct, TRUE);
     cartRemove(cart,"ajax");
     jsInlineFinish();
     }
 else
     {
     // htmlNoEscape() below lets the <span> through, so the labels themselves have to be
     // escaped here - they come from trackDb, which a track hub controls