Browser gates: prove the server is THIS build before believing it

Five gates took their measurement from whatever answered a default localhost
port. On this machine that port belongs to whichever worktree started first:
:4173 is wt-packet's server, :4898 is its repo root. Measured today — an hour of
probing reported on a build that was not on disk here, and the mistake was only
caught because a marker string was in the file and absent from the response.

"Answers 200" and "is serving this checkout" are different questions, and only
the second one licenses a verdict. _serves-this-tree.mjs already asked it (it
exists because the gospel-resources gate hit this and spent three runs reporting
on the wrong tree) but only three callers used it. Wired into the five that
measure a served page and had no such check:

  check-provider-switch        request tally per host — the wrong tree's tally
  check-maplibre-layer-drops   silent layer drops — invisible by nature
  check-filter-width-stable    width divergence — would accuse untouched CSS
  check-facet-changes-map      data flow on facet change
  check-packet-browser         imports the renderer FROM the server

Only a genuine MISMATCH skips. "No manifest to compare" is a different
precondition (an unbuilt checkout) that each gate already speaks for, and
turning that into a skip would silence these on the builds they should run on.
A mismatch exits 0, like the unreachable case, because it is a precondition and
not a product finding — but it prints which, so nobody reads it as a pass.

CONTROLS, each fired, per gate: correct server -> PASS with a real number
(provider-switch 19 -> 5 mapbox requests, drops 17/17, filter-width identical,
facet 2107 -> 1209 dots, packet-browser headings match); a served tree whose
manifest differs by one key -> SKIP naming "serves a DIFFERENT build"; no server
-> the pre-existing unreachable SKIP, unchanged.

One correction found by running it rather than by reading it: the first wiring
passed the LEAF PAGE URL where the helper wants the ORIGIN, so it looked for the
manifest under the page's own path and skipped every run for the wrong reason.
node --check cannot see that; only running both controls can.

KNOWN COST, not hidden: in a wave with no server of this tree running,
check:provider-switch and check:packet-browser now SKIP where they used to
report a green about another worktree. That is the honest reading, not a
regression, and the runner prints "evaluated nothing" for them. Next increment
is to let these gates serve this checkout themselves so they measure again.
This commit is contained in:
doxa-maps-88655c70 2026-09-24 17:00:11 +00:00
parent 7fd8e4545c
commit 6602cd1399
5 changed files with 102 additions and 0 deletions

View file

@ -53,6 +53,26 @@ import { chromium } from 'playwright'
// (`bun run serve` on 4173) to run it against a built leaf or the cap-5 shell.
const BASE = process.env.BASE_URL || 'http://127.0.0.1:5173'
const PATH_ = process.env.PROBE_PATH || '/'
// ⚠ THE SERVER THAT ANSWERS IS NOT NECESSARILY THIS CHECKOUT. Several worktrees of this repo
// run on one machine and a default port belongs to whichever started first — measured
// 2026-09-24 on :4173. This gate reports on how a map's source data changes when a facet is
// picked, so a run against another tree describes somebody else's data flow. The fingerprint
// is public/doxa-maps/manifest.json; a dev server serves it straight off disk, so this stays
// quiet in the normal `bun run dev` case and only speaks when the bytes really differ.
{
const { servesThisTree } = await import('./_serves-this-tree.mjs')
const { fileURLToPath } = await import('node:url')
const { dirname, resolve } = await import('node:path')
const repo = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const tree = await servesThisTree(BASE, repo)
if (/DIFFERENT build/.test(tree.why)) {
console.log('FACET-CHANGES-MAP: SKIP — the server is not serving THIS build.')
console.log(` ${tree.why}`)
console.log(' Point BASE_URL at this checkout (bun run dev, or bun run serve).')
process.exit(0)
}
}
const TAB = process.env.PROBE_TAB || 'research-dashboard'
const SELF_TEST = process.argv.includes('--self-test')
// WHICH HALF OF THE FIX TO REVERT in --self-test. Default 'colour' reverts ONLY the paint, leaving

View file

@ -36,9 +36,29 @@
* NEGATIVE-TESTED: restoring `flex:1 1 160px; max-width:240px` on .filter-dropdown makes widths
* diverge and this fails by name, naming the before/after arrays.
*/
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { chromium } from 'playwright'
import { servesThisTree } from './_serves-this-tree.mjs'
const REPO = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const BASE = process.env.BASE_URL || 'http://127.0.0.1:4173/doxa-maps'
// ⚠ "ANSWERS 200" IS NOT "IS THIS BUILD". This machine runs several worktrees of one repo and
// the default port belongs to whichever server started first — measured 2026-09-24: :4173 was
// another worktree's. This gate compares filter widths BEFORE and AFTER a tab change and fails
// on divergence, so a run against someone else's tree can report a width regression in code
// that was never touched here. Separate that from the unreachable case below, which is already
// a SKIP: both are preconditions, neither is a product finding.
{
const tree = await servesThisTree(BASE, REPO)
if (/DIFFERENT build/.test(tree.why)) {
console.log('FILTER-WIDTH: SKIP — the server is not serving THIS build.')
console.log(` ${tree.why}`)
console.log(' Serve this checkout (`bun run serve`) and pass its port as BASE_URL.')
process.exit(0)
}
}
const WALK = `(sel)=>{const o=[],s=new Set();const w=(r)=>{if(!r||s.has(r))return;s.add(r);try{o.push(...r.querySelectorAll(sel))}catch(e){};for(const e of r.querySelectorAll('*'))if(e.shadowRoot)w(e.shadowRoot)};w(document);return o}`
async function measure(page, label, activateTab) {

View file

@ -40,6 +40,7 @@ import { chromium } from 'playwright'
import { readFileSync, existsSync } from 'node:fs'
import { resolve, join, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import { servesThisTree } from './_serves-this-tree.mjs'
// Portable by construction — see check-no-absolute-paths.mjs. Never hardcode a worktree.
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
@ -52,6 +53,22 @@ if (!existsSync(MANIFEST)) {
console.log('MAPLIBRE-LAYER-DROPS: SKIP — no built manifest (run `bun run tree` first).')
process.exit(0)
}
// ⚠ REACHABLE AND MINE ARE DIFFERENT QUESTIONS. This machine runs several worktrees of one
// repo and the default port belongs to whichever server started first — measured 2026-09-24:
// :4173 was another worktree's, and this gate reported PASS 16/16 about a build that was not
// on disk here. The layer-drop family is silent by nature (MapLibre omits a layer instead of
// throwing), so a verdict taken from the wrong tree is exactly the kind nobody can catch by
// looking. A mismatch is a precondition, not a product failure: SKIP, but name it.
{
const tree = await servesThisTree(BASE, ROOT)
if (/DIFFERENT build/.test(tree.why)) {
console.log('MAPLIBRE-LAYER-DROPS: SKIP — the server is not serving THIS build.')
console.log(' ' + tree.why)
console.log(' Start `bun run serve` in this checkout and pass its port as BASE_URL.')
process.exit(0)
}
}
const manifest = JSON.parse(readFileSync(MANIFEST, 'utf8'))
const leaves = []
for (const b of manifest.bundles || []) {

View file

@ -56,6 +56,24 @@ try {
console.log(' python3 -m http.server 4898 (or set PACKET_ORIGIN)')
process.exit(0)
}
// ⚠ AND IT MUST BE THIS CHECKOUT'S ROOT. Several worktrees of this repo run on one machine,
// so a port that answers may belong to another one — measured 2026-09-24 on :4173, where a
// browser gate reported on a build that was not on disk here. This gate imports the shared
// renderer FROM THE SERVER, so the wrong origin means it verifies someone else's renderer
// and says nothing about this change. Precondition, not a product failure: SKIP and name it.
{
const { servesThisTree } = await import('./_serves-this-tree.mjs')
const repo = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const tree = await servesThisTree(ORIGIN + '/public', repo)
if (/DIFFERENT build/.test(tree.why)) {
await b.close()
console.log('PACKET-BROWSER: SKIP — the server is not serving THIS checkout.')
console.log(` ${tree.why}`)
console.log(' Serve THIS repo root and re-run (PACKET_ORIGIN=http://127.0.0.1:PORT).')
process.exit(0)
}
}
const html = await pg.evaluate(async ({RENDER, STRINGS, LANG, ORIGIN, API, ISO3, ZONE}) => {
const [{ createPacketRenderer }, strings, norm] = await Promise.all([
import(RENDER),

View file

@ -12,7 +12,12 @@
// not calling its tile servers, and only the request log shows that.
//
// Usage: node internal/check-provider-switch.mjs <leafUrl>
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { chromium } from 'playwright'
import { servesThisTree } from './_serves-this-tree.mjs'
const REPO = resolve(dirname(fileURLToPath(import.meta.url)), '..')
// ⚠ NOT named `URL` — that shadows the global URL constructor, so every
// `new URL(request.url())` below throws and the host tally stays EMPTY while the
@ -39,6 +44,28 @@ const LEAF = process.argv[2] || DEFAULT_LEAF
console.log(' python3 -m http.server 4173 --directory public (or set BASE_URL)')
process.exit(0)
}
// ⚠ REACHABLE IS NOT THE SAME QUESTION AS MINE. Several worktrees of this repo run on one
// machine and the default port belongs to whichever started first — measured 2026-09-24:
// :4173 was another worktree's server, and an hour of probing reported on a build nobody
// here had changed. This gate's whole verdict is a request tally from a page; a tally taken
// from someone else's page is a confident statement about the wrong code.
// A mismatch is a PRECONDITION, not a product failure, so it SKIPs (exit 0) like the
// unreachable case above — but it says which, so nobody reads it as a pass.
// The helper wants the ORIGIN that serves public/, not the leaf page — passing the page URL
// makes it look for the manifest under the page's own path and report "nothing serving it",
// which would SKIP every run for the wrong reason. (Measured: it did, on both servers.)
const tree = await servesThisTree(new URL(LEAF).origin, REPO)
// Only a genuine MISMATCH skips. "No manifest to compare" (an unbuilt checkout) is a
// different precondition and the reachable check above already speaks for it — turning
// that into a skip here would silence the gate on exactly the builds it should run on.
if (/DIFFERENT build/.test(tree.why)) {
console.log('PROVIDER-SWITCH: SKIP — the server is not serving THIS build.')
console.log(` ${tree.why}`)
console.log(' Serve your own tree and re-run, e.g.:')
console.log(' bun run serve (then BASE_URL=http://127.0.0.1:PORT)')
process.exit(0)
}
}
async function run(provider) {