Commit graph

8 commits

Author SHA1 Message Date
Doxa Maps
ba619b78bf The engine rule reads as it is everywhere it is written: auto is MapLibre, token or not
Since 2026-09-28 resolveMapProvider returns MapLibre for 'auto' whether or not a token is present;
only an explicit provider selects Mapbox. Four comments still said "Mapbox when a token is
available": useMapProvider.js's own header (and its note on the `token` argument),
useMapInstance.js at the provider option, check-provider-switch.mjs's contract note, and the
PROVIDER note that generate-tree writes into every leaf page.

The leaf page's note now says what the page does: it finds a token first only when one is on offer,
because place search uses one on either engine, and the element decides the engine. The 64 leaf
pages are regenerated and differ only in that note. The three JS files change in comments only
(compared minified, without renaming).
2026-09-29 11:04:22 +00:00
Doxa Maps
fbc517c94a A browser gate refuses to launch outside the browser slot; check:browser-gates-guarded keeps it that way
Only the browser seat runs browsers, and only inside internal/browser-slot.sh.
That rule was remembered, not enforced: an agent ran a gate it took for a
headless source check and it launched Chromium outside the slot.
internal/lib/playwright-guarded.mjs is now the one way a repo file reaches
playwright: its launch(), launchPersistentContext(), launchServer(), connect()
and connectOverCDP() exit 75 (NOT MEASURED) with a message naming
browser-slot.sh unless DOXA_BROWSER_SLOT is set, and browser-slot.sh exports it
for the command it runs, so a bash -c grandchild inherits it. All 72 files that
imported playwright switch to it (internal/lib/browser.mjs and 64 internal gates;
plugin-designs scripts: doxa-research-map 3, my-upg-100-list 2,
research-map-plus-grid 2, research-dashboard 1): a mechanical import swap.
check:browser-gates-guarded (gates:pre) fails a direct import;
bun run browser-gates lists the 97 scripts that launch a browser. isBrowserGate
moves to internal/lib/browser-gates.mjs so the runner and the check share it.
2026-09-28 08:20:21 +00:00
Doxa Maps
be0a9efecf Token gates follow the auto engine: no token ships, and an explicit engine beats an available token
The build config no longer bakes a Mapbox token, and the default engine is 'auto' (Mapbox only when a
token is available). Two gates still assumed the old contract.

- check:nested-token: with the build config's token empty (the shipped default), no leaf and no bundle
  may carry a pk. token, and every leaf must carry the runtime engine ladder (EXPLICIT / HAS_TOKEN),
  so with no token it draws MapLibre, keyless. A builder who bakes their own token gets the original
  check. Fails on the pre-rebuild tree, which still has baked tokens.
- check:provider-switch: the Mapbox baseline is now ?provider=mapbox with a fake host token (its
  requests are aborted, never sent); the switch run adds ?provider=maplibre to the same token (an
  explicit engine must win); a third run requires the no-token default to make no Mapbox requests.
2026-09-26 03:37:53 +00:00
doxa-maps-88655c70
44ef31b2c6 Tooling reads the tree's name from the build config, in one place
The output tree's name has always been configurable (vite.doxa-maps-build-config.json "name"),
but 61 tooling files ignored it and hardcoded 'doxa-maps' (181 literals), so a rename would have
moved the build while the gates kept reading the old folder.

- internal/_tree-name.mjs reads it once (validated as one URL-safe path segment; falls back to the
  historical name) and exports TREE_NAME / TREE_NAME_RE; `node internal/_tree-name.mjs` prints it
  for shell scripts.
- A codemod rewrote 171 literals in internal/ to TREE_NAME; the regex literals, messages with
  backticks, atomic-public-swap.sh (fails loudly if the name cannot be read), build-lock.sh and
  vite.config.js (its duplicate readMfeName is gone) were done by hand.
- The name is unchanged: old and new tooling produce all 1408 files under public/ byte for byte.
  Code lines still naming the tree in internal/ + vite.config.js: 2, both intended (the helper's
  fallback, the private-repo-name pattern in public-hygiene-check).

This renames nothing visible; the eventual rename becomes a one-line config change.
2026-09-25 16:25:22 +00:00
doxa-maps-88655c70
9acd4f8865 Queue the browser gates, and let them serve their own tree
Two problems, one change.

LOAD. Seven agents each running a wave put 7+ chrome-headless renders on one box;
measured today it reached load 47 with 8GB of swap, and a browser gate that is
swapping measures the swap, not the product. The wave runner now holds browser
steps while 1-min load is at or above LOAD_MAX (12) and takes a coarse flock
before each one, so they queue instead of dogpiling. Thresholds, cadence and the
20-minute cap are bundler-3's wait-for-quiet.sh verbatim, and BROWSER_GATE_LOCK
selects the rendezvous, so every agent lands on one lock whichever entry point
they use. Static gates are untouched: in gates:post that is 38 free, 4 queued.

The load wait is taken ONCE per wave, not once per gate. Per-step it multiplied
the same hold by the number of browser gates — an hour of a wave doing nothing,
which is how a load fix becomes a reason to stop running gates. The flock is
what serializes; the wait only decides when to join the queue.

Browser steps are identified from the gate FILE (a playwright import), not a
hand-kept list that goes stale the first time someone adds a gate — the one
missing from such a list is exactly the one that rejoins the dogpile. `verify` is
caught too: it imports no playwright itself but spawns three gates that render,
and it already marks them browser:true, so that declaration is honoured rather
than duplicated.

COVERAGE. Yesterday's guard made check:provider-switch and check:packet-browser
SKIP in every wave, because their default ports (4173, 4898) belong to other
worktrees on this machine. Skipping was honest but a gate that skips every time
is a gate nobody runs, so on a mismatch they now start a short-lived server over
THIS checkout and measure that instead — the point was never the port, it was
being able to prove which build answered.

MEASURED: a served tree whose manifest differs by one key but has all the same
pages -> "serves a DIFFERENT build", then "serving this checkout on :28384
instead", then a real verdict (16 -> 5 mapbox requests). packet-browser likewise,
once it re-navigates to the new origin — a dynamic import across origins is
refused by a plain static server, which cost one failed run to learn.
gates:post 40/42, the only skips being the two that genuinely have no input
(packets-all, zone-census); it was 4 before this.

The lock path default is product-named. The shared file several shells export
carries an internal code name, and check:no-codename refuses to let a repo we
hand over carry one — it caught this commit's first draft.
2026-09-24 17:43:32 +00:00
doxa-maps-88655c70
6602cd1399 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.
2026-09-24 17:00:11 +00:00
doxa-maps-88655c70
baf691e8cf provider-switch gate: run by default, instead of only when someone types a URL
Some checks are pending
build-gate / build (push) Waiting to run
check:provider-switch is a good gate — it counts requests BY HOST rather than
reading window globals (which stay defined after a swap), and it allows exactly
the api.mapbox.com URLs the generated leaf declares in its own head, so a
half-fix at 18 requests cannot pass.

It had a package.json entry and was in no wave, because it REQUIRED a URL
argument and exited 2 without one. So it only ever ran when somebody typed a
URL — the same present-but-never-executed shape check:packets-all had before it
was given a fixture.

Wiring it as-is would have turned every server-less build red rather than
silently green, which is the less common half of that failure. It now defaults
to the upg-100 leaf and SKIPs in the form the wave runner recognises, so the
skip is counted and named instead of grading as a pass or an error.

gates:post 36 -> 37, README re-synced. Verified both ways: 37/37 with a server,
and 27/37 passed / 6 SKIPPED on a fresh checkout with none.

Found while re-verifying BUG-UPG100-IGNORES-THE-PROVIDER-SWITCH from scratch
rather than trusting its done status. The card holds: ?provider=maplibre drops
Mapbox traffic 19 -> 5 on the upg-100 leaf, and all 5 are the page's own
declared preloads, so zero runtime calls remain. The same run independently
reproduced the four residual URLs in BUG-LEAF-PAGES-PRELOAD-MAPBOX-EVEN-UNDER-
MAPLIBRE, which stays untaken — its fix is in generate-tree, the tree verb
bundler-1 owns. When those preloads go, this gate's allowance drops to 0 and it
holds the new floor with no edit.
2026-09-24 15:30:35 +00:00
doxa-maps-88655c70
e1553df458 test(provider): ?provider=maplibre must swap the ENGINE, measured by request host
Some checks are pending
build-gate / build (push) Waiting to run
Gates BUG-UPG100-IGNORES-THE-PROVIDER-SWITCH. my-upg-100-list builds its map RAW
with `new gl.Map` instead of going through useMapInstance, so the page-level
switch — which resolves inside that composable — could not reach it. The card
measured 19 requests to api.mapbox.com at ?provider=maplibre while five sibling
leaves swapped cleanly.

★ IT COUNTS NETWORK REQUESTS BY HOST, not globals. `window.mapboxgl` can be
present because the leaf page preloads it while the map is actually DRAWN by
maplibre, and the reverse is equally possible. What the switch exists for is not
loading a proprietary engine and not calling its servers, and only the request
log shows that.

The allowance is DERIVED from the leaf's own HTML — count the distinct
api.mapbox.com urls the generated page declares, plus one for a stylesheet that
gets fetched twice — rather than hardcoded. Asserting merely "fewer than the
default" would pass a half-fix at 18 of 19; hardcoding 5 would pass a leaf that
preloads 4 and leaks 1 runtime call.

★ THE GATE'S OWN FIRST VERSION MEASURED NOTHING AND PASSED. `const URL =
process.argv[2]` shadowed the global URL constructor, so every
`new URL(request.url())` threw, the host tally stayed empty, and it cheerfully
reported "0 mapbox requests" on a page making 19. It now names the variable LEAF,
counts what it could not parse, and FAILS if the default run records no requests
at all — because a silently empty tally is indistinguishable from success.

Passing on three leaves today: upg-100-list 19 -> 5, doxa-simple-map 21 -> 4,
countries-map 24 -> 4, with every residual being the page's own <link>/<script>
preloads. Confirmed to fail when the allowance is tightened by one.
2026-09-24 04:47:54 +00:00