1fbda5badde574c10884e5339fdda1f5b7495990
braney
  Thu Sep 24 13:45:30 2026 -0700
docent: regression script for the Sessions page Replace keeping who can load it, refs #38311

rm38311 saves a private session from the save card, saves over it with the
private box unticked, and checks that the row still has its lock.  It passes on
hgwbeta (v504) and genome-test, and fails on hgw0 (v503).

Docent changes it needed:
- a fill: verb, to type into any form field
- login: no longer dies when a navigation is still under way during its
bad-password check, which is what broke it on ticket parks
- a park is driven on its https port, because hgLogin posts its form to
https:// on the port it was reached on; the parks' self-signed certificate
is accepted on loopback targets only
- hgw0 maps to hgcentral, and a park's https port finds its hg.conf

diff --git src/hg/utils/docent/targetConf.js src/hg/utils/docent/targetConf.js
index 3443057b4f0..e34e1b2159b 100644
--- src/hg/utils/docent/targetConf.js
+++ src/hg/utils/docent/targetConf.js
@@ -50,33 +50,35 @@
 function hgConfFor(server) {
   let u;
   try { u = new URL(server); } catch (e) { return null; }
   const host = u.hostname;                            // 127.0.0.1 keeps its dots
   const name = host.split('.')[0];                    // hgwdev-braney out of the FQDN
   if (name === 'hgwdev' || name === 'genome-test') return '/usr/local/apache/cgi-bin/hg.conf';
   if (name.startsWith('hgwdev-'))                     // a sandbox or a demo browser
     return `/usr/local/apache/cgi-bin-${name.slice('hgwdev-'.length)}/hg.conf`;
   if ((host === '127.0.0.1' || host === 'localhost') && u.port) {
     // A ticket park from `ts`: its port is in the registry, and the frozen hg.conf sits
     // under the ticket's own directory. A park is the one target whose conf is NOT the
     // live sandbox's, which is the whole reason for parking it.
     const root = process.env.TS_ROOT || path.join(os.homedir(), 'ticketSandboxes');
     let reg;
     try { reg = fs.readFileSync(path.join(root, 'ports.tsv'), 'utf8'); } catch (e) { return null; }
+    // The registry holds the http port; the same park answers https on that port + 1000.
+    const httpPort = String(u.protocol === 'https:' ? Number(u.port) - 1000 : Number(u.port));
     for (const line of reg.split('\n')) {
       const f = line.split('\t');
-      if (f[1] === u.port) return path.join(root, f[0], 'cgi-bin', 'hg.conf');
+      if (f[1] === httpPort) return path.join(root, f[0], 'cgi-bin', 'hg.conf');
     }
   }
   return null;
 }
 
 // hg.conf as hg/lib/hgConfig.c reads it: `include` pulls another file in relative to the
 // including one, `delete` drops a name, and a later assignment wins over an earlier one
 // (parseConfigLine hashAdds and cfgOption reads the most recently added). So the value
 // this returns is the EFFECTIVE one -- a sandbox conf that sets nothing still shows what
 // it inherits from the shared conf it includes.
 //
 // Callers print only the settings they name. The includes lead to hg.conf.private, which
 // holds database passwords.
 function readHgConf(file, out = new Map(), seen = new Set(), depth = 0) {
   if (depth > 10 || seen.has(file)) return out;
@@ -92,30 +94,31 @@
       for (const name of line.slice(6).trim().split(/\s+/)) out.delete(name);
     else {
       const eq = line.indexOf('=');
       if (eq > 0) out.set(line.slice(0, eq).trim(), line.slice(eq + 1).trim());
     }
   }
   return out;
 }
 
 // Servers whose hg.conf is on another machine, so it cannot be read and has to be known.
 const CENTRAL_BY_HOST = {
   'genome.ucsc.edu': 'hgcentral',
   'genome-euro.ucsc.edu': 'hgcentral',         // its own database of the same name
   'genome-asia.ucsc.edu': 'hgcentral',         // ... and so is this one
   'hgwbeta.soe.ucsc.edu': 'hgcentralbeta',
+  'hgw0.soe.ucsc.edu': 'hgcentral',            // the RR node that takes a release first
 };
 
 // Which hgcentral a server reads. Read from its hg.conf wherever that is possible, because
 // a sandbox may say so for itself: of the personal confs on hgwdev today, 45 set
 // central.db to hgcentraltest and two do not (hgcentralgsid, hgcentralbeta).
 function centralDbFor(server) {
   const file = hgConfFor(server);
   if (file) {
     const db = readHgConf(file).get('central.db');
     if (db) return db;
   }
   try { return CENTRAL_BY_HOST[new URL(server).hostname] || null; } catch (e) { return null; }
 }
 
 // ---------- the account a `login:` step signs in with ----------