Commit graph

9 commits

Author SHA1 Message Date
Doxa Maps
2c397d25ef Source comments state design history and measurements as facts, without quoting or naming anyone
Comments in library/ and internal/ that quoted a person, attributed a decision to one, used first person,
named fleet agents or carried private host names now state the same technical fact in neutral words:
poster-render (including the comments it writes into generated posters), packet-render, the poster and
research scripts, PosterWall, DataTable, the legends, the layouts and the gates. The network-policy note
states the policy itself. Behaviour is unchanged: a few printed gate messages and reason labels drop an
agent name, the poster-render integrity pin follows its comment-only edit, and series.json changes only in
its _meta and _why notes. The historical poster design snapshots (library/scripts/posters/snapshots/),
which nothing reads, are removed.
2026-10-01 00:37:17 +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
3711c590c9 One browser slot on this machine: a second browser run queues instead of racing
(cherry picked from commit 928d38d445)
2026-09-26 03:34:13 +00:00
doxa-maps-88655c70
405db45f19 browser-slot: a disk floor, and an infrastructure crash is NOT MEASURED, not a red gate
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
2026-09-25 20:52:19 +00:00
doxa-maps-88655c70
456b920fb6 browser-slot: a long gate declares its own hold limit; check:host-locale-contract takes 2700 s
A full check:host-locale-contract run loads every leaf twice and hit the 900 s default three times
(exit 75, NOT MEASURED). An env prefix in its package.json script cannot help: the wrapper reads its
limit before the script's environment exists. So a gate declares it in its header
(@browser-slot-max-hold <s>); the wrapper reads it for 'node <file>' and 'bun run <script>' alike.
The caller's BROWSER_MAX_HOLD still wins, a declaration is capped at 3600 s, and every other
command keeps the 900 s default. Tested on a private slot dir: declared 3 s -> killed at 3.1 s;
caller override 1 s -> 1.1 s; bun run form -> 3.2 s; 99999 -> capped to 3600.
2026-09-25 16:25:22 +00:00
doxa-maps-88655c70
544819624f browser-slot: a slot is held for at most BROWSER_MAX_HOLD seconds, and only by the wrapper
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
A probe that awaited map.once('load') with no timeout held slot 1 for 89 minutes on 3 s of CPU,
and every other browser gate on the box queued behind it and timed out as NOT MEASURED.

- The command now runs as a child under `timeout --kill-after` (default 900 s, BROWSER_MAX_HOLD);
  timeout signals the whole process group, so the chrome a probe spawned dies with it. 124/137 are
  reported as 75 NOT MEASURED, naming the command.
- fd 9 (the slot lock) is closed for the child. With exec, the command and every grandchild
  inherited it, so an orphaned process kept the slot locked after its parent exited — measured
  with the previous wrapper: the next caller got 75 after its wait.
- Proven on a private slot dir (1 slot, 5 s limit): sleep 99999 freed at 5.07 s with 75 and the next
  waiter ran at 5.58 s; a TERM-ignoring child killed at 7.1 s; after a command that leaves an orphan
  the slot is free in 261 ms; exit codes and stderr pass through unchanged.
2026-09-25 13:46:33 +00:00
doxa-maps-88655c70
cfdbea7f9c Host locale: gate the profile-config contract, forward ?lang= into staging embeds, stop losing gate stderr
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
- check:host-locale-contract: every leaf loaded with ONLY profile-config.locale="ar" must draw at
  least half the Arabic the same page draws at ?lang=ar; regions-list is the positive control.
  Fails on this commit for 18 leaves (bundles that never provide appI18n, and every composite).
- check:staging-lang: the staging page at ?lang=ar embeds its leaf with lang=ar and the Gospel map
  renders gospel.mode.gap in Arabic. staging-forge.js (iframe + open link) and
  staging-reference-v2.html now forward ?lang= / ?locale= via withLang(); fails on the parent.
- browser-slot.sh: `exec 9>f 2>/dev/null` redirected the whole script's stderr, and the gate
  exec'd after it, to /dev/null — FAIL lines and "not measuring" messages were being discarded.
  The redirection is now scoped to the open.
- verify-map-paint: the two regions map leaves join the default list (both paint, local and live).
- Vision card reads ?lang= / ?locale= like the rest of the library.
2026-09-25 12:18:26 +00:00
doxa-maps-88655c70
be3620ef1c browser-slot.sh: stop overwriting the caller's BASE, and let a waiter take any slot that frees
Two defects, both reproduced before the fix and re-measured after.

1. It assigned BASE for its lock path. A variable the caller exported
   stays exported in bash, so every gate run through the slot saw
   BASE=/tmp/doxa-maps-browser-gate.lock. Seven gates read
   process.env.BASE; check:upg-map-data-painted printed "SKIP — nothing
   served at /tmp/doxa-maps-browser-gate.lock/…" and exited 0. Renamed
   to SLOT_BASE. Proof: with BASE=/x set, a child printed the lock path
   before and /x after.
2. With every slot busy, waiters queued on slot 1 alone, so a slot that
   freed was never taken and two slots behaved as one queue. It now
   polls every slot with flock -n every 2 s until the deadline and
   still exits 75 on timeout. Proof, in a private slot dir with slot 1
   held 12 s and slot 2 freed at ~3 s: a caller waited 11.5 s before and
   4.3 s after. A free slot is still taken at once (66 ms).

check-upg-map-data-painted: its unreachable-server SKIP exited 0 and
its wrong-tree refusal exited 2. Both now exit 75, the repo's "not
measured" code. Its existing tree-identity check is what makes the
:4173 default safe, since another agent's tree there is refused.
2026-09-25 10:03:16 +00:00
doxa-maps-88655c70
430c06d978 Browser gates: two slots with a memory floor, not one mutex
The single rendezvous made every agent's browser proof wait behind one chrome —
the queue sat 10-19 deep for hours. Exclusivity was never the real constraint:
MEMORY is, at roughly 300-500MB per headless chrome. So the rendezvous becomes a
counting semaphore.

internal/browser-slot.sh takes the first free slot with flock -n, in a fixed
order, and queues on slot 1 only when all are busy. Before taking any slot it
requires MemAvailable above a floor, because a free slot on an out-of-memory box
is an invitation to be killed mid-measurement — which then reads as a flaky gate
rather than as the machine it actually was. Default 2 slots, 2500MB floor, both
overridable.

It keeps the exit-code contract that the last commit established: 75 is
EX_TEMPFAIL, "not measured", distinct from 0 and from any gate's own 1. The
runner already reports 75 as ⊘ and excludes it from the failure count.

CONTROLS, all fired:
  two takers      both run concurrently
  a third         waits, and takes a slot the moment one frees
  short wait      exits 75 rather than 1, so a full queue is never a red gate
  floor above RAM exits 75 immediately, naming the MemAvailable it saw
  inner exit 3    passes through as 3 — the wrapper never rewrites a verdict
  through a wave  two browser gates, 3.5s wall for 3.9s of work: overlapping

The legacy single lock is deliberately no longer consulted: taking it alongside
the slots would collapse two slots back into the one queue this replaces. Agents'
own wrappers should call internal/browser-slot.sh so everyone shares one counter
— and it lives in the repo precisely because the old shared path carried a name
check:no-codename forbids us to ship.
2026-09-25 07:37:26 +00:00