Commit graph

16 commits

Author SHA1 Message Date
Doxa Maps
1b19b7478a Register the control-inset gate, now that it has been run and proven
verify-browser-all's rule is that every gate listed there was run before it was
listed, so this waited for a real run rather than being registered on the
strength of the code reading correctly.

Measured, on the browser seat: the positive arm PASSES 9/9, and the mutation arm
-- the maplibre selector inside bottomControlInset broken to a name that matches
nothing -- FAILS on cases 1 and 5, the two whose pages carry only a maplibre
attribution. Cases 2, 3, 6 and 7 pass under the mutation by design: they assert
the inset is 0, so a selector that stops matching cannot make them fail. They
guard the opposite error, reserving space that is not there.

Registered in all three places: the script, its //scripts description, and
verify-browser-all's list. Deliberately NOT in gates:pre -- every assertion rests
on getBoundingClientRect, so it needs a browser and exits 75 NOT MEASURED
without a slot, which is never a pass.
2026-09-29 14:35:29 +00:00
Doxa Maps
f574c7586f The browser suite runs both engine gates, each listed after its first real run
The suite lists a gate only after it has been run, so these waited for one. On 5b05f019e, in the
browser slot:
  check:auto-engine      PASS. no token -> MapLibre, 0 Mapbox requests; a host token alone ->
                         MapLibre, 0 Mapbox requests; ?provider=mapbox + the token -> Mapbox,
                         carrying it
  check:engine-contract  PASS. 2 providers x 3 load states: the engine asked for, or a visible
                         failure, never a substitute
Both entries record those results. check:engine-contract cannot demonstrate a failure at runtime
without the defect rebuilt into a bundle; its falsifiable arm is the source gate's self-test, so
check:no-silent-engine:self-test now runs in gates:post beside the gate it proves. It was chained
nowhere.
2026-09-29 11:04:22 +00:00
Doxa Maps
3575631935 The one-command browser suite runs smoke:legend-info, which crashed on load for four days because only a hand ran it 2026-09-29 09:18:56 +00:00
Doxa Maps
d314c44043 internal/ keeps the reason and drops the speaker: no gate quotes or cites the Driver
Every reference to the Driver in internal/ is reworded the same way as in
library/: the reason stays, the speaker and the quote go, an open decision
reads "provisional, pending a product decision", and agent names and decision-
sheet rows are dropped. Where a gate printed the word, its message now names
the reviewer or the ruling instead.

Three spots were code, not comments, and changed with their callers:
- the element census's allowlist field `driver` is now `ruling`, in the gate
  and both presets (same validation: match, ruling, date and card required);
- check:i18n-never-translated's DRIVER-GATED marker is DECISION-PENDING, in
  the entries and in the regex that counts them (its output is otherwise
  byte-identical);
- the package.json description of the census says "ruling".

public-hygiene-check keeps its two patterns for the word: they are what keep
it out of the served folder.
2026-09-29 05:26:25 +00:00
Doxa Maps
7d1ed8a4fc Assert that MAPBOX drew, which nothing measured
RT8 goal (b). Two gates already touch the engine and neither asserts this:
check-leaf-engine-per-provider proves MAPLIBRE purity (zero *.mapbox.com
requests under ?provider=maplibre) and that a map still draws, but never forces
Mapbox; check-provider-switch does load ?provider=mapbox — with a FAKE token, so
the engine LOADING was measured and a tile RENDERING never was, on one leaf.

So "Mapbox works" was unmeasured across the seven production tabs. This measures
it, and refuses to pretend when it cannot.

WHAT IT READS, and why each is the stronger property:
  · THE ENGINE off the canvas that drew — .mapboxgl-canvas vs .maplibregl-canvas
    — never from which globals exist. A leaf asked for Mapbox that quietly draws
    with MapLibre looks identical to success in a screenshot and passes every
    paint check; a peer script that merely loaded Mapbox proves nothing.
  · THAT IT DREW via queryRenderedFeatures() > 0. A canvas at the right size is
    not a map: this repo already had a country map render fully blank while "a
    real GL canvas exists at 1200x780" passed.

THREE EXIT STATES, BECAUSE TWO WOULD FORCE A LIE. On this sandbox the Mapbox
/v4/ composite raster endpoint and country-boundaries both 403 — proven outside
the browser with no Referer, with 127.0.0.1 and with localhost, so it is tileset
ENTITLEMENT, not the embed (PRODUCTION-MATRIX:235). Failing on that would
manufacture work for every owner; passing on it would be false. It exits 75 NOT
MEASURED and names the endpoint. No token, or a non-pk. token, is also 75 — a
fake token is exactly the false green this gate exists to refuse.

THE NEGATIVE ARM is in the same file: ?provider=maplibre must draw with MapLibre.
If it does not, the engine check cannot tell the engines apart and every pass
above it is unfalsifiable.

★ TARGETS COME FROM THE MANIFEST, AND SELECTING ON isMap ALONE LOST A TAB.
Measured: it found 6 of the 7 curated tabs. research-map-plus-grid (Simple
Research) is a nested-elements-mfe with isMap:false whose CHILD holds the map, so
it would have been skipped in silence — the exact class of miss this gate is
meant to catch. A composed tab now counts if it is isMap ITSELF or composes a
profile that is: 7 of 7.

Registered in package.json (script + description) and in verify-browser-all's
GATES array — deliberately NOT gates:pre, because it needs the browser seat.
Verified by re-reading the parsed package.json, and check:no-orphan-gates names
only the pre-existing check-gospel-whiteboard.mjs, not this.
2026-09-29 00:26:56 +00:00
Doxa Maps
cdcdd84cf9 verify:browser-all reads a gate's exit 75 as NOT MEASURED, not as a failure
It counted any non-zero exit as FAIL, so a gate that could not measure (no
browser slot, a server mid-build) read as a broken product, the same bug
upg100 found in check:packets-all once the browser guard made gates refuse
outside the slot. Exit 75 is now NOT MEASURED, shown as such, and a run with
no failures but any unmeasured gate exits 75 itself (INCOMPLETE), never green.
2026-09-28 09:07:29 +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
a5ab04ec75 Measure the basemap, in a real embed, on the token the page ships
"The map renders" had never been measured. Every browser gate here
forces a Mapbox token onto the page from a shared file; that token is
restricted and 403s on the basemap, while the leaf bakes a different
one that works. The gates then counted PINS — which are drawn from our
own GeoJSON and paint with no token at all — so they passed on a
basemap the probe itself had broken.

This gate forces nothing. It serves the leaf on one port and a host
page on another so the iframe is genuinely cross-origin, loads it as a
stranger's browser would, and takes evidence two ways: Mapbox responses
counted by status, and colour variety in a slice of the canvas. The
gospel map passes — 9 tile responses, none denied, and the screenshot
shows coastlines, ocean shading and country labels.

The negative control runs every time, and the check reports PROVED
NOTHING rather than a pass if the control succeeds. That is not
decoration: the first two attempts at the control did nothing at all.
A page-level init script never reaches a cross-origin iframe, and even
at context level it was ignored, because this leaf injects `tk`
straight into the element and only falls back to window.MAP_TOKEN —
so it is immune to the forced-token hazard by construction. The
control had to move to the wire. With the token rewritten there, 6 of
8 tile requests are denied and the basemap vanishes, leaving only the
pins and choropleth: the picture that shows exactly why a pin count
was never evidence of a map.
2026-09-24 19:13:46 +00:00
doxa-maps-88655c70
fb7ed4566d Browser gates now prove the server is serving THIS checkout
The gospel-resources gate was found adopting whatever answered on the default port,
which on this machine was an unrelated process from a different worktree. It reported
on a build that was not the one on disk: first a PASS, then a mount timeout that read
exactly like a regression in code nobody had touched. "Answers 200" and "is serving
this checkout" are different questions, and only the second one is the gate's job.

That fix was local to one gate. The same default sits under the rest of them, so it is
now one shared helper, internal/_serves-this-tree.mjs, fingerprinting
public/doxa-maps/manifest.json — which generate-tree rewrites on every build and which
names every bundle and profile in the tree. Different bytes means a different build:
another worktree, or a server someone forgot to restart. Both produce the same lie.

Wired into verify-browser-all, which hands ten gates their base URL and would have
passed the wrong tree to all of them, and into check-a11y-button-names. Both now refuse
with a NOT MEASURED exit rather than producing a confident wrong answer. The gospel
gate keeps its behaviour of starting its own server instead.

Verified in both directions: against the foreign 4173 each prints "serves a DIFFERENT
build than public/" and exits 2; against this tree the aggregator proceeds to its ten
gates and the gospel gate passes.

ONE REPORTED NUMBER WAS WRONG BECAUSE OF THIS. The a11y result recorded earlier in this
flight, "810 buttons, 810 with an accessible name", was measured against the foreign
tree. Re-run against this checkout it is 825 buttons, 825 with an accessible name,
still passing across 21 leaf pages. The conclusion held; the evidence had not been
about this build.

Audited the rest of this flight's browser evidence rather than assuming: the 41 probe
scripts in _qa-cycle all target port 5399, which is served by
serve-strict --directory <this worktree>/public, and the manifest served there is
byte-identical to the local one. Those results stand.
2026-09-24 12:54:35 +00:00
doxa-maps-88655c70
3d3586b5f9 Commit the map check, so the browser path survives this flight
Every browser check written for this bundle lived in _qa-cycle/, which is gitignored: a
dozen probes proving the pins render, the sheet opens with real links and the deep link
filters the table — all of which disappear with the working tree. check:coverage-consistent
guards the DATA and cannot see the map at all. The repo has carded orphaned gates twice;
this is the same loss one step earlier, a gate that was never committed.

It asserts, against the SHIPPED tree through the production-parity server: the element
mounts and exposes its state handle (a stale-SRI leaf silently refuses to execute, which
looks identical to a mount failure); every pin in the source is RENDERED, not merely
loaded; a tap opens a group with a NAMED language; the table deep-link carries that
group's own ISO code; and the gap filter leaves only tier 0.

★ IT TOOK THREE TRIES TO MAKE IT WORTH TRUSTING, and each failure is the point.

FIRST it was FLAKY — 1 run in 3. Clause 4 waited a fixed 900ms after clicking the filter,
but setFilter is applied asynchronously, so it queried mid-transition and still saw tier 1
and 2. It now waits for the map's own idle event. 5 of 5 runs clean. A gate that fails a
third of the time on correct code is a gate that gets switched off.

SECOND its doctrine check had a SAMPLE SIZE OF 1. It validated the two URLs of the one
group it happened to open — 1 of 2,685 codes — so a jw.org link planted anywhere else
passed cleanly. MEASURED: the injection did not fail the gate until the scan covered the
whole index. It now checks all 3,550 links, and fails if the index carries none at all,
because a scan over nothing passes vacuously.

THIRD, adversarially tested end to end: with jw.org injected on code `tel` the gate exits
1 naming the publisher; restored, it exits 0; the file's md5 is unchanged either side.

Registered in package.json and in verify-browser-all's GATES array — check:no-orphan-gates
confirms it, and check:quoted-scripts passes. Without a server it SKIPS loudly with the
command to start one, rather than passing quietly.
2026-09-24 07:31:01 +00:00
doxa-maps-88655c70
7e1baac4a1 Hand an inheritor a token that works, and stop claiming one is present without checking
The golden path told a new author to fetch a token from doxa.life/api/maps/token.
Measured: that token is URL-restricted to doxa.life, so a tile request without
that Referer returns 403 and every basemap in their smoke test renders blank
while the pins draw fine. That is exactly the symptom the Driver reported as
loading the pins but not the underlying map, and it is why tokenUrl was emptied
in the build config the day before. The first command in the hand-off doc
reproduced a known bug.

It now reads the token from the build config, which is the source that works:
same tile request, config token 200, endpoint token 403. That is a pk. public
key baked deliberately, protected by URL restrictions rather than secrecy, and
check:public-hygiene allows pk. while failing the build on sk. and tk. -- so it
is safe to read and never a secret to guard. The doc shows the command, not the
key.

The aggregator had the same bug and a worse symptom: it fetched from the same
endpoint and printed "token present", which meant a string had been obtained,
not that it worked. So a run could report a present token while every map gate
measured a blank basemap. It now prefers the config token, falls back to the
endpoint, and validates whichever it gets with one tile request.

Verified in all three states: the config token reports "present and serves
tiles", a fabricated token reports "tiles 401 - basemaps will be blank", and the
old endpoint token reports "tiles 403 - URL-restricted?". The banner reports
what was measured rather than what was fetched.
2026-09-23 04:09:07 +00:00
doxa-maps-88655c70
aa196db1d8 Refuse to serve a half-built tree, so a gate cannot mistake one for a regression
A build removes public/doxa-maps before rebuilding it, so a browser gate running
during one reads an empty tree and reports a confident wrong answer.
verify-shell-scroll said it could not find the dashboard-plus-grid tab twice in
ten minutes while a swarm rebuilt underneath it. That reads exactly like a real
regression and cost three ticks of this flight.

The server now answers 503 for anything under /doxa-maps/ while the manifest is
absent, with a body naming the cause. A 503 rather than a 404 is deliberate: 404
means "this is not here", which a gate may reasonably report as a product defect,
while 503 with a reason cannot be mistaken for a finding. Assets outside the
bundle tree are unaffected.

The check belongs in the server because all nine gates fetch through it. Guarding
one chokepoint means none of them can misread a torn tree and none of them needs
editing, where having every gate take the build lock is nine changes plus a rule
every future gate has to remember.

The aggregator already refused to start against a missing tree, but only at
startup, and a full sweep runs for seventeen minutes. It also blamed a missing
server for a 503, sending the reader to start one that was already running; it
now names the real cause. The gap the server closes is the one that actually bit:
a single gate run directly, at any moment, including mid-sweep.

Verified by hiding the manifest in place: a leaf returns 503 with the explanation
while /assets/ still returns 404, and both return to 200 when it is restored.
2026-09-23 03:52:30 +00:00
doxa-maps-88655c70
b2fc0e4dda Make the shell-scroll gate runnable, and catch orphan gates
Some checks are pending
build-gate / build (push) Waiting to run
verify-shell-scroll.mjs was written and negative-tested, then left with no
package.json entry and no importer, so nothing could ever run it again. It
also defaulted to localhost:8899 -- an ad-hoc port from an earlier session --
so it failed with ERR_CONNECTION_REFUSED for anyone else and read as a broken
gate rather than a missing server. Now check:shell-scroll, defaulting to
BASE_URL or 127.0.0.1:4173 like every other browser gate, and listed in
verify-browser-all's GATES array.

check:no-orphan-gates fails any gate-shaped file in internal/ that nothing can
invoke. The repo carded this once already; it recurs because writing a gate
and registering one are separate acts and only the first feels like work.

Not every unreferenced file is a lost gate, so the rule is not "everything
must be wired": theme-flash-probe is a dated diagnostic for two fixed bugs,
worth keeping as a reproduction recipe. It now carries an @one-shot tag saying
so, which makes the author state which kind of thing they wrote.

Two self-referential defects in the first version of that gate, both from
reading evidence out of documentation:

- it cited theme-flash-probe.mjs in its own header as an example, then grepped
  for importers, found itself, and declared the orphan reachable. The gate
  written to catch orphans made an orphan pass by mentioning it. A gate must
  never be its own witness, so it now excludes its own file.
- it matched @one-shot anywhere in prose, including its own paragraph
  explaining the tag, and reported "1 tagged" when nothing had been tagged.
  The tag must now be a marker at line start.

Negative-tested: removing check:shell-scroll from package.json fails by name.

The scroll fix itself is NOT done. contain:layout and .vtl-host overflow-y are
confirmed in the built bundle, and a real wheel gesture still moves the grid
0px with 2586px mounted and unreachable. The gate is what caught that; the
diff looked correct.
2026-09-22 17:55:43 +00:00
doxa-maps-88655c70
a1005869ce Anchor the filter dropdown in every host, not just standalone
Some checks are pending
build-gate / build (push) Waiting to run
The menu is position:fixed with viewport coordinates. That resolves against the
viewport only while nothing establishes a containing block for fixed
descendants. VerticalTabsLayout -- the cap-5 shell that hosts every plugin as a
tab -- sets contain:layout paint on .vtl-content, inside .vtl-main{left:256px}.

Measured at 1600x900, same component, two hosts:
  standalone research-dashboard ...... dLeft=0   dTop=4    six of six
  inside plugin-studio dashboard tab . dLeft=256 dTop=111  every one

256 is the rail width and 111 the header height. Reported as the menu landing
"in a random spot further down to the right"; it was never random.

An earlier probe measured six triggers at four viewport widths and passed, so
the card was closed while the bug was still there. Every number was accurate.
They were answers about a surface the user never looks at -- widening across
more widths could not have found it, because the variable was the host.

Fix measures the containing block instead of assuming it: a fixed probe at 0,0
inserted in the menu's own parent reports the origin the menu resolves against.
When nothing captures, that origin is 0,0 and the standalone numbers are
unchanged bit-for-bit.

Not fixed by dropping contain from the layout: it is load-bearing there,
stopping a rail collapse from reflowing a heavy mounted map.

check:fixed-anchoring measures both hosts and treats an unmeasured host as a
failure. Negative-tested: reverting the subtraction fails the studio host with
dLeft=256 while standalone still passes -- it fails exactly where the old probe
passed.

Also cards a second symptom of the same cause, not yet fixed: fixed-position
modal scrims written inset:0 stretch to the content pane instead of the
viewport, leaving the rail clickable behind an open modal.
2026-09-22 17:22:23 +00:00
doxa-maps
f132d2e056 Put share verification back in the browser suite
Some checks are pending
build-gate / build (push) Waiting to run
I excluded smoke:share from the aggregate two ticks ago and the reason was wrong.
Every clause of it was true — the script takes required arguments, and its
gallery mode does need the vite dev server — but the conclusion did not follow. I
had tried exactly two invocations, bare and --plugin-gallery, and never --url,
which the script's own header documents first. It runs fine against the static
tree: 8 of 8 pass, each Share button reopening the correct custom element with a
live canvas.

That is the same shape I logged as an anti-pattern last tick, now found in my own
work: a not-applicable verdict is more durable than a wrong bug report, because
it closes the question. It cost the suite its highest-consequence surface for two
ticks. A share URL is the one artefact a partner pastes into an email, and it
fails silently.

It runs as smoke:share-all, carrying its own eight --url flags and --browser
chromium, since Firefox headless has no WebGL on this box and the default engine
stops. The eight leaves are the set derived by grepping for ShareButton, not a
guess. The simple-map case is the discriminating one: it shares to a different
leaf than the one opened, so the deep link is really being followed rather than
an SPA fallback rendering something plausible.

Two shell traps on the way. A VAR=x prefix does not apply to $VAR on the same
line, because the shell expands the argument before the assignment scopes to the
command, so the URLs came out with an empty base; deferring through sh -c fixes
it. And a glob in a block comment closed the comment early.
2026-09-22 10:30:41 +00:00
doxa-maps
d463cb6161 Run every browser gate from one command
Some checks are pending
build-gate / build (push) Waiting to run
All seven browser gates were orphans: each had a script, and not one was invoked
by anything else. They were reachable only by someone who already knew the name
to type, which is exactly the person who does not need the list. This repo has
been bitten by this before — the Gate-1 acceptance test existed, worked, and was
invoked by nothing, so it could never report anything. An orphan gate is worse
than a missing one because it looks like coverage.

The data guards already solved this; verify-all.mjs says it plainly — a guard
nobody runs is documentation. That file has no reference to playwright or to the
built tree. It is the data half. This is the browser half.

Every gate listed here was run before it was listed, following the rule the
earlier audit set: listing a program you have not executed puts unvalidated
entries in the hand-off surface. All six pass, and the aggregate now runs them
end to end: capabilities, embeds, map paint, the Gate-1 chain, accessible names,
and phone navigation.

smoke:share is excluded on purpose and says so. It takes required arguments and
its gallery mode reads a registry only the vite dev server serves, so listing it
would make the suite fail for something that is not a defect.

Preconditions are asserted up front and exit 2, so a missing server reads as a
setup problem rather than six mystery failures.
2026-09-22 09:15:00 +00:00