Commit graph

41 commits

Author SHA1 Message Date
Doxa Maps
a3225e072f check:poster-wall-host-seams: load any .vue PosterWall imports, and prove it with arm L
The gate compiled PosterWall.vue alone, as a client component, and runs
Vite without the Vue plugin. A child component imported by PosterWall
(a section component, which a refactor is about to add) would fail to
load, turning the gate red on correct code.

One resolveId/load hook (enforce 'pre', so Vite's resolver does not load
the raw SFC first) now client-compiles PosterWall and every .vue it
imports, each at its real path, so their own relative imports resolve as
in the build; the earlier '../../' path rewrite is gone. Arm L loads a
parent fixture that renders a child .vue by relative import
(internal/fixtures/poster-wall-host-seams/) and asserts the child's text.
It fails without the hook and without 'pre'.
2026-10-02 06:39:49 +00:00
Doxa Maps
2f163e04c1 Large side-lane sheets: photos print in the sheet's own document, and a raster never draws a broken image
Measured on the 60x96 print: every photo beside a name rasterised as the browser's broken-image glyph. An SVG
rasterised through <img> is an isolated document that fetches no external resource, and none of the people-group
photo hosts (media.joshuaproject.net, joshuaproject.net, images.peoplegroups.org and its resizer,
peoplegroupspublic.blob.core.windows.net, s3.doxa.life) sends CORS, so a photo cannot be inlined as data: in the
browser either.
- poster-raster.js: rasterPoster drops every <image> whose href is not data: (the basemap tiles are data: and stay),
  so a download shows the paper, never the glyph. hasExternalImages() says when a sheet carries such photos.
- PosterWall: printing a generated sheet that carries photos prints THE SHEET ITSELF through printDocument, where
  the photos load, on its own @page (60in 96in), instead of a raster shrunk onto letter. Same HUD steps.
- check:side-lanes: photo arms (0 external images reach the raster; the wall prints such a sheet as a document),
  each with a poison in its self-test.
- check:side-lane-photo-pixels: the pixel side, a browser gate (a data: photo draws red; 99/99 external slots stay
  flat; without the strip 0/99 do). Declared on-demand until its first browser-seat run.
2026-10-01 09:45:33 +00:00
Doxa Maps
f526ef3d4f P: merge 9ed84bd56 2026-10-01 00:56:02 +00:00
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
2eaea5cedf P (repo): neutral wording in the gates and docs, and no private-process checks
Comments, messages and docs only; no check logic changes.
- The role-word gate is renamed check-no-internal-role-nouns (and its baseline fixture). Same
  logic: it builds the word from parts so the file does not carry it, and a planted line in
  title case and in capitals still fails it while WebDriver and "GPU driver" do not.
- public-hygiene-check: the two role patterns are built from parts (the regexes are byte-
  identical), the header loses the forge and date wording, and the prayer comment loses its
  decision date. Its output on the tree is identical in normal and --strict modes.
- landing-preconditions, check-no-public-binds, check-published-tree-weight: no fleet tool
  name, no "this box", no forge name in comments and messages.
- Removed: check-backlog-refs, check-backlog-status-enum, check-done-evidence,
  check-verification-tags and check-dxlife-untouched (they police a private backlog and
  workspace), with their scripts, docs, gate-list entries and orphan-baseline entry, and the
  committed needs-eyes pages (check-embedded-rtl --emit writes them on demand).
- package.json docs: no agent names. Docs: the stale check mentions and the snapshot
  references are gone (the snapshots themselves leave in the studio P commit).
gates:pre: PASS 115, FAIL 0, NOT MEASURED 1 (browser seat).
2026-10-01 00:25:38 +00:00
Doxa Maps
cc65f40c1e Gate hygiene: three gates:post reds on development, and two things that hid failures
- check:no-orphan-gates stopped at internal/check-grid-merge-defaults.mjs, which had no script. It
  asserts defaults of three grid props that are not on development (the grid merge has not landed),
  so it is registered ON DEMAND with that reason, not chained red. Past that first arm, its chain arm
  found check:grid-image-loading and check:retry-fetch run by no chain; both depend only on code and
  pass in under 300 ms, so they join gates:pre.
- A tracked vite.config.js.timestamp-*.mjs (395 KB, eight absolute paths) failed check:no-abs-paths.
  Removed, and ignored at the source: vite writes one beside its config while it bundles it.
- check:library-index: documentation/reference-library-index.md regenerated for retryFetch.js.
- run-parallel printed a failed step's output only with --verbose: the unbraced else bound to the
  inner if. Braced; a failing step now prints why.
- The Pages workflow ran bun run build without BUILD_ALLOW=1, so build-lock refused it with exit 75
  and the workflow ran no gate at all. A CI runner is the only builder on its machine; it opts in.
2026-09-29 18:35:37 +00:00
Doxa Maps
83fa4da4f4 Drop the region-paint gate's on-demand entry now that it is chained
The entry was marked temporary, to be deleted when the gate joined
gates:post. 6a1860539 chained it; the entry stayed behind.
2026-09-29 17:42:45 +00:00
Doxa Maps
de6d60378e Merge origin/development into studio/integration 2026-09-29 16:38:05 +00:00
Doxa Maps
2ff7d2f17b Merge origin/development into studio/integration 2026-09-29 16:01:38 +00:00
Doxa Maps
0417e46fbd Merge remote-tracking branch 'origin/development' into bundler-2/handoff
# Conflicts:
#	internal/check-poster-layout.mjs
#	library/composables/poster/useMapPoster.js
#	package.json
2026-09-29 16:00:18 +00:00
Doxa Maps
212ec20816 Merge remote-tracking branch 'origin/development' into bundler-2/handoff
# Conflicts:
#	package.json
2026-09-29 15:54:16 +00:00
Doxa Maps
b1173d48ab Merge remote-tracking branch 'origin/development' into upg100/merge 2026-09-29 15:27:41 +00:00
Doxa Maps
62afbdcdf3 check:ported-globals-identical compares only the ported rules, not the whole stylesheet
The gate hashed each component's entire emitted CSS, so any unrelated style change
in these four files (Regions giving three :host rules position:relative) failed it,
and could pass only by re-baselining a guard that exists to prove something else.
It now hashes what library/styles/portedGlobals.js emits, found in the executed CSS:
each cyrillicHeading and strongMedium rule with the selectors in front of it, and
each scrollbar-gutter declaration with its rule's selector.

Re-baselined on the tree the old gate passed on. Proven both ways: re-scoping
GoalCard's strong rule, changing the module's Cyrillic letter-spacing and dropping
a file's ported rules each fail it; --write refuses to baseline an empty block.
2026-09-29 15:03:05 +00:00
Doxa Maps
f8d0a7d9c0 check:ported-globals-identical compares only the ported rules, not the whole stylesheet
The gate hashed each component's entire emitted CSS, so any unrelated style change
in these four files (Regions giving three :host rules position:relative) failed it,
and could pass only by re-baselining a guard that exists to prove something else.
It now hashes what library/styles/portedGlobals.js emits, found in the executed CSS:
each cyrillicHeading and strongMedium rule with the selectors in front of it, and
each scrollbar-gutter declaration with its rule's selector.

Re-baselined on the tree the old gate passed on. Proven both ways: re-scoping
GoalCard's strong rule, changing the module's Cyrillic letter-spacing and dropping
a file's ported rules each fail it; --write refuses to baseline an empty block.
2026-09-29 14:56:52 +00:00
Doxa Maps
9d00aa3db4 check:snippets-draw: every published snippet, pasted verbatim on another origin, must draw (awaiting its first run)
Ported from rega's snipgate and mbxcheck probes (card GATE-EVERY-PUBLISHED-SNIPPET-DRAWS-A-MAP). Two scripts so the keyless arm never waits on a token: check:snippets-draw pastes each production embed.script and requires a MapLibre map to draw (a hooked instance with its style loaded via a setter trap on the engine global, a sized canvas, tiles 2xx, no failure note, 0 page errors); check:snippets-draw:mapbox does the same over embed.scriptMapbox with only the token filled from MAP_TOKEN, NOT MEASURED without one and never printing it. Composites that open on a list (regions-mfe, upg-100-mfe) are pressed to their map view under the settle rule first, since a hidden pane still draws a canvas. The browser slot is checked before any server starts, and the tree server is spawned --exit-with-parent. Registered and declared on demand until rega's first run; it joins verify-browser-all after.
2026-09-29 14:56:52 +00:00
Doxa Maps
844fc60d0e Merge upg100/group-set-standard into development (handoff)
# Conflicts:
#	library/scripts/posters/packet.mjs
2026-09-29 14:53:36 +00:00
Doxa Maps
bdb74ff69f Merge remote-tracking branch 'origin/development' into bundler-2/handoff 2026-09-29 14:50:51 +00:00
Doxa Maps
71050cbc8d Merge branch 'bundler-2/pair1' into bundler-2/handoff 2026-09-29 14:50:16 +00:00
Doxa Maps
12078103d1 check:no-orphan-gates sees gate FILES outside internal/: a plugin's own gate that nothing runs fails
The file walk read internal/ only, and the chain arm counts package.json NAMES, so a plugin-local
check-*.mjs that nobody registered was invisible to both: on 2026-09-29 six were run by nothing
(dx-shepherd's census; exp's four have since been registered). Every tracked gate-shaped file under
plugin-designs/ or library/ must now be run by PATH from a script or another file's code, carry
@one-shot, or sit in the shrink-only `files` baseline for its owner.

Two rules keep it honest. Comments are stripped before searching, since a file mentioned in prose is
not run by it. And a basename counts only where no other gate file shares it:
library/scripts/research/verify-map-paint.mjs (199 lines, run by nothing) shares its name with
internal/verify-map-paint.mjs (342 lines, what verify:map-paint runs), so a name match let one file's
registration vouch for the other. Self-tested on planted cases (unregistered, comment-only, shared
basename). On the real tree it finds 4, now baselined for their owners:
  library/scripts/research/verify-map-paint.mjs            likely a stale copy
  plugin-designs/doxa-research-map/scripts/check-reduced-motion-jumps.mjs
  plugin-designs/gospel-resources/scripts/verify-translator-org-links.mjs   a data tool? @one-shot or register
  plugin-designs/research-map-plus-grid/scripts/check-ui-strings-have-keys.mjs
The chain arm's writer now keeps every key of the shared baseline file.
2026-09-29 14:35:28 +00:00
Doxa Maps
f331c962c0 Declare the region-paint gate as on-demand while it is half-adopted
Registering a check that no chain runs turns the orphan guard red: it fails any
gate-shaped script that nothing reaches and nobody declared. That guard is
right, and it caught this the round after it was built -- "registered but not
chained" is precisely the state it exists to force into the open, and it was
the state this branch was in.

The gate cannot be chained yet without holding the shared run red over another
plugin owner's unlanded work: it asserts the regions idle paint has one source,
and only one of the two maps has adopted the library export so far. So declare
it on demand with the reason, in the file that requires one.

The entry is temporary and says so. It is deleted in the same landing that
chains the gate, once the second map's half lands.

Verified the declaration satisfies the guard rather than silences it: blanking
the reason makes the guard fail again, naming this entry and the file it is in.
2026-09-29 12:55:06 +00:00
Doxa Maps
b899e6887a The reference baseline reaches 0: the last two lines were a hygiene checker's own patterns
Every owner's sweep has landed. The two remaining lines were public-hygiene-check.mjs's pattern list, which must spell the role word in order to catch it on the way out, as this gate's own file must. It is exempt by name in a one-file list with its reason, not by any wider rule. The baseline is now empty: from here, every line that names the role is a new reference and fails.
2026-09-29 12:44:25 +00:00
Doxa Maps
a9e560e6f5 The reference baseline shrinks 28 -> 2 after the owners' capitals sweeps
--shrink drops only what is gone; nothing is added.
2026-09-29 11:57:01 +00:00
Doxa Maps
7ae80e3810 The coverage gate's poison self-test runs beside its chained arm
check:coverage-consistent:chained sets the link-age threshold out of reach. The proof that this narrows its scope and does not disarm it is the poison arm: its clock is computed RELATIVE to the threshold, so all 15 clauses (A-O, L included) still fail when poisoned at any setting (regions measured it at 99999). That proof was an orphan; it now runs on every landing, like the other gates' self-tests. 1.6 s.
2026-09-29 11:56:47 +00:00
Doxa Maps
b142fe1178 Two more gates join gates:pre honestly: dxlife walks only the clones, and the calendar stays on demand
check:dxlife walked the whole root-dx-life umbrella, so git worktrees of doxa-life (wt-rg-*) and
a dangling one (research-map-clean, whose .git points at a path that no longer exists) read as
+4,524 "added" files and held it red while the clones were untouched. It now skips any directory
whose .git is a FILE beginning `gitdir:` (keyed on that file, not on `git worktree list`, which
cannot see the dangling one) and .cache with the other generated dirs, and compares the baseline
only within that scope. Result against the 2026-09-20 baseline: 2,844 clone files, +0 -0 ~0. So the
law has held since that date, and the baseline stays as it is: re-taking it would reset the evidence.
Nothing is deleted; research-map-clean may hold the only copy of its work. With no clone on the
machine it reports NOT MEASURED (75) instead of exit 2, so chaining it cannot fail other machines.

check:coverage-consistent fails when the oldest verified link is over 45 days old: its verdict
depends on the calendar, and chained it would turn gates:pre red on 2026-11-08 with no change
(regions measured the margin). Every other clause depends on code and data and is worth running
on every landing, so gates:pre runs check:coverage-consistent:chained (the age knob set out of
reach). The plain script is declared on demand as the freshness run, with that reason. Its poison
self-test still proves every clause can fail.

gates:pre 94/0/2 of 96. The orphan baseline shrinks to 105; 7 checks are declared on demand.
2026-09-29 11:56:47 +00:00
Doxa Maps
9226aa7cff Twenty orphan checks join the chains; five more are declared on demand, each with its reason
From regions' triage of the headless orphans, every candidate re-run on this round's tip before
it was chained (all exit 0; the slowest, no-conflict-markers, 8.2 s):
  gates:pre   click-sheet coverage-consistent dashboard-filter-a11y filter-multivalue hover-channel
              kit-options-reachable layout-key-namespaced legend-mirrors map-unmount-race
              no-conflict-markers series-isolation snapshot-guard stale-sweep-claim
              surfaces-read-the-tier translator-contacts   (source readers)
  gates:post  embed-base link-coverage no-public-binds shared-reached tree-weight   (built tree)
stale-sweep-claim reports SKIP while 1 of 1184 gap codes is undecided: it measures once the pool is
fully read, and the wave shows it as not measured until then, never as a pass.

Declared on demand instead: catalog-currency and published (they read the published host),
composite (minutes of rebuilds; landing's fresh step does this per landing), hardcoded-english
(report-only while its findings are triaged; its self-test is chained), and shot-fonts (it checks
the SCREENSHOT MACHINE's fonts, so chained it would fail a contributor's gates for their fonts).
Two proposals were declined: check:settled stays on demand (it fails inside any build by design,
and passing standalone is how it was wrongly chained before), and shot-fonts as above.

The orphan baseline shrinks 131 -> 106. gates:pre 93/0/2 of 95, gates:post 91/0/16 of 107.
2026-09-29 11:56:47 +00:00
Doxa Maps
afc3e9edda Every check is run by a chain, declared on demand with a reason, or listed for its owner
check:no-orphan-gates only REPORTED scripts outside the chains, and the list grew to 87 main checks.
That day two of them hid real defects: check:i18n-never-translated, while a dead key identical in all
11 catalogues sat on development for hours, and check:auto-engine, which asserted a retired engine
rule that nothing ran. The report also matched by substring, so check:hardcoded-english counted as
run because its :self-test is in gates:pre.

The arm now decides. Every check:/verify:/smoke: script must be:
  reached    by exact name from gates:pre, gates:post, the browser suite's GATES or the build,
             following what those scripts `bun run`;
  on demand  in internal/fixtures/on-demand-checks.json WITH its reason (check:settled, whose
             reason this gate already documented: it fails inside any build by design);
  baselined  in internal/fixtures/orphan-checks-baseline.json, today's 131 orphans, for their
             owners to chain, declare or delete. That list only shrinks (--shrink).
A new unchained script fails by name. A self-test runs first on every invocation. Proven two-sided on
the real tree: a planted unchained script exits 1 naming it; swapping exact matching for the old
substring test makes the self-test refuse; restored, it passes.
2026-09-29 11:56:46 +00:00
Doxa Maps
f32cf8c4ed The reference baseline shrinks 54 -> 28: the owners' capitals sweeps removed 26 lines
Lines swept on development since the capitals form joined the gate: research-dashboard, people-groups-grid, my-upg-100-list, regions-dashboard and others. --shrink drops only what is gone; nothing is added.
2026-09-29 11:04:34 +00:00
Doxa Maps
395fdf69e0 The reference gate sees the capitals form too: 42 lines had passed it unseen
The gate matched the role word only as `Driver`. Lines written in capitals, such as `DRIVER: "…"`,
`THE DRIVER'S …` or a `…-DRIVER-REPORTED` card id, never registered: 42 of them sat in shipped text
while the owners' sweeps, measured by this gate, read as nearly finished. research-dashboard alone
carries 24, most of them verbatim quotes.

The pattern is now `\b(?:Driver|DRIVER)\b`. A GPU or device driver, WebDriver and DRIVER_MERGE still
do not match, and the self-test now plants the capitals form as well. Three of the 42 are reworded in
this round. The other 39 join the baseline in a one-time re-measure, the only time it has grown:
15 of the 21 baselined lines remain, the 6 that repo's docs sweep removed are gone, and every added
key is a capitals-only line. It holds 54 lines in 20 files and only shrinks from here. Two of the
additions are code terms rather than citations (a "CAT-1 DRIVER CONTRACT", a "BATCH-UNION DRIVER");
their owners can reword them in their sweeps.
2026-09-29 10:07:29 +00:00
Doxa Maps
75ea0adec0 doxa-maps-spa: its comments say what the code does and why, not who asked for it
The shell's comments carried 23 of the reference baseline's lines, one more written in capitals that
the baseline's pattern never saw, and four internal card ids. Each is reworded to keep the engineering
reason: the 404s, the grey pane, the scroll trap, the device rule and the curated order all stay
explained. Quotes became the rule they stated.

Comments only. The script (esbuild, whitespace and syntax only, no renaming), the template (compiled
without comments) and the styles (comments removed) are identical before and after. The baseline
shrinks by the 23 lines that are gone.
2026-09-29 10:07:28 +00:00
Doxa Maps
755c1023af The reference baseline shrinks to the 44 lines still present, after the owners' sweeps 2026-09-29 09:19:15 +00:00
Doxa Maps
51ef6f7ec4 One phone flag per map, and the leaf page follows it as well as the Gallery
A map says how it behaves on a narrow screen once, in its <profile>.meta.json:
"mobile": "desktop-only" (blocked on a phone) or "larger-screens" (opens,
under a dismissible notice). Absent means phone-ready. Until now the rule
lived only in the Research Gallery, as two hard-coded id sets, so a map
opened standalone on its own leaf page on a phone got neither the block nor
the notice.

- profile-meta.mjs knows the vocabulary; generate-tree and the dev manifest
  carry the flag into each profile's manifest entry, and generate-tree into
  the leaf page's BASE_CONFIG (with the map's name for the desktop-only
  sentence).
- ProfileLoader mounts library/components/LargerScreenNotice.vue on a screen
  under 640px: for desktop-only the profile is never mounted and a centred
  message says why; for larger-screens a slim in-flow bar sits above the map
  (it cannot cover the map's controls), dismissible per profile for the
  session, in the element's own locale and direction. Inside the Gallery it
  stays quiet: every Gallery pane now carries data-doxa-gallery.
- One text: the Gallery's two sentences moved into the library catalogue as
  messages.largerScreenNotice / desktopOnlyNotice (+ noticeDismiss), all 11
  locales moved as they were, and the Gallery reads them from there.
- The Gallery derives its rules from the manifest's flags. On a phone it
  mounts nothing until the manifest has landed: its baked first tab (Big
  Picture) would otherwise mount there before the flags say it is
  desktop-only.
- dashboard-plus-grid is desktop-only and gospel-internet/board is
  larger-screens, the two sets the Gallery held; nothing changes for either.
- check:mobile-gating now asserts the vocabulary, that each flagged leaf page
  carries its flag, the Gallery's derivation and its closed-before-manifest
  rule, that Big Picture stays desktop-only, the 11 locales, and the
  notice's mounting. Its self-test plants one failure per rule.
2026-09-29 07:31:16 +00:00
Doxa Maps
8cdf4c53fa The reference baseline drops the two lines reworded on development since the last shrink 2026-09-29 05:31:55 +00:00
Doxa Maps
7b494facf5 The browser-side generator keeps the reason and drops the speaker
library/packet/poster-render.js and posterSheets.js arrive in this landing
carrying the generator's comments as they stood before the sweep: two lines
named the Driver and one said "he changed the SHAPE". They are reworded as
the sweep reworded them in generate.mjs, and two pronouns the sweep left
behind (SearchFilterHeader, check-filter-width-stable) now say what was
asked instead of who asked. The baseline drops the two generate.mjs lines
that moved out.
2026-09-29 05:26:27 +00:00
Doxa Maps
34fd21dd5d The last library reference drops the speaker, and the baseline shrinks to what is left
library/layouts/app-wrapper-material/index.html carried the one library
reference the sweep's file filter missed (it looked at scripts, components
and docs, not .html): reworded the same way, comment only.

The reference baseline then shrinks from 505 to 147 lines, so nothing
reworded in this landing can come back. What remains is in the owners'
plugins, documentation and skills.
2026-09-29 05:26:26 +00:00
Doxa Maps
c62db6ee4c The reference baseline shrinks to the 505 lines still present, so the 181 already reworded cannot return 2026-09-29 03:47:01 +00:00
Doxa Maps
7d2decab5b Shipped text stops gaining references to the Driver: a forward-only gate with a baseline that may only shrink
The standing rule is that comments, docs and UI strings say what the code does and why, never who said
what. Several hundred existing lines break it; owners remove them in one sweep per area before the public
giveaway. Until then check:no-new-driver-references (gates:pre) fails any NEW line that refers to the
Driver and baselines the rest, keyed by file and line text with counts rather than line numbers, so
parallel sweeps only ever shrink it. It matches the role word, not a GPU or device driver, and its
self-test proves both that and that a new line beside baselined ones is isolated.
2026-09-29 03:46:37 +00:00
Doxa Maps
fc3b64bbe6 Migrate the regions pages onto portedGlobals, and fix a Cyrillic rule that was inert
upg100's harness recorded my two files as NOT MEASURED because its stubs threw on
them. Both were FIDELITY gaps — the stub modelled states that cannot occur — so
they are fixed by making the stub more like the browser, never by relaxing a
comparison:

  · `.value` was undefined, so a component reading an INJECTED ref threw
    "Cannot read properties of undefined" (region-dashboards.vue:199,
    cfg.value.regionSlug — ProfileLoader always provides that object).
  · the stub threw on ANY string coercion, because a Proxy over a function has no
    primitive conversion. That alone produced regions-list's "Cannot convert
    object to primitive value".

`.value` is now the EMPTY STRING, which is the one shape that serves both real
cases honestly: a locale ref's .value IS a string, so `${locale.value || 'en'}`
correctly yields 'en'; and reading a property off '' returns undefined instead of
throwing, so an injected-config read falls through its own `|| ''` exactly as in
the browser. Symbol.toPrimitive makes any remaining stub render empty.
Measured components: 2 -> 4, with no NOT MEASURED left.

★ AND THE MIGRATION FOUND A REAL BUG, which is the part worth reading.
regions-list's Cyrillic heading rule ended with a trailing comma and NO
declaration block, so the concatenation emitted:

    [lang="ru"] .rl-title, [lang="ru-RU"] .rl-title,regions-list

A selector list with nothing to apply — the Russian heading font was silently
never applied — AND it swallowed useShadowStyles' style-id argument as CSS,
leaving that sheet unlabelled. Braces stayed balanced, so
check:shadow-css-parses could not see it, and it passed 80/80 both before and
after. cyrillicHeading('.rl-title') replaces it and emits a real rule.

SO THE BYTE-COMPARE SPLITS, deliberately, and I did not paper over it:
  region-dashboards.vue  IDENTICAL — cyrillicHeading('.rd-title','.rd-h2')
                         emits exactly the literal it replaced, verified
                         character-for-character before the swap. A pure
                         de-duplication.
  regions-list.vue       CHANGED, and the change IS the fix. The old bytes were
                         the bug, so its baseline is re-taken rather than
                         preserved. Preserving it would have pinned a broken
                         rule forever, which is the one thing a byte-identical
                         gate must not be used for.

NOT MIGRATED, on purpose: strongMedium(). The helper emits bare `strong{...}`
while region-dashboards emits `.rd-root strong{...}` — a narrower selector.
Swapping it would widen the rule to every <strong> in the shadow root, a
behaviour change disguised as a de-duplication. scrollbarGutter() is likewise
left inline: both files carry it inside a larger .rd-scroll/.rl-scroll rule, not
as a standalone declaration the helper could replace.

Both files now gated (owned:true) instead of recorded-only, so a future edit that
changes their emitted CSS fails rather than being noted.
2026-09-29 02:35:35 +00:00
Doxa Maps
443b163682 Share the ported _globals.scss rules through one module
Seven shadow-root components each carry their own copy of DX Life's
base/_globals.scss rules, because page CSS cannot reach into a shadow
root. Measured first: where the copies overlap they are byte-identical
(the Cyrillic stack 3x, strong{font-weight:500} 2x, scrollbar-gutter 2x),
so this is deduplication, not a fix.

The module exports FUNCTIONS, not strings, and the measurement forced
that: the six copies of the Cyrillic rule share one declaration but have
six different subjects (.rd-goal-card__title, .rd-h2, .rd-title,
.region-card__title, .rl-title x2). A fixed rule string would have to
pick one class and silently stop styling the other five. The caller owns
its selector; the module owns the declaration.

cyrillicHeading takes a selector LIST, not a selector. GoalCard styles
one heading class; RegionSectionCard groups two into a single rule
sharing one declaration block. A one-selector signature emits two rules
instead of one -- the same CSS to a browser, but not the same
stylesheet, and the bar here is unchanged, not equivalent.

Migrated GoalCard and RegionSectionCard. regions-dashboard's two files
port the same rules and stay with their owner; the harness records them
so that migration can run against this same baseline.

The img border-radius rule (_globals.scss:80) is documented as
deliberately NOT ported, because an invisible omission is one that gets
"fixed" later.

PROOF: internal/check-ported-globals-identical.mjs executes each
component's setup, captures the string handed to useShadowStyles and
hashes it. The emitted CSS is byte-identical across the refactor
(987d94b1cd66110d, 897d948b17b21efe, both captured from 45e31f102).

The harness's first version hashed the SOURCE of the call and was
wrong: this refactor replaces a literal with a function call, so the
source changes by construction -- a text-hash gate fails a perfect
refactor and cannot tell that from a broken one. It measured the
artifact when the property under test was the output. It failed exactly
that way on first run.

Shown capable of failing, four ways: dropping letter-spacing, a wrong
font name, 500->600, and splitting the grouped rule into two. All four
go red; restoring goes green. Components that cannot be evaluated are
reported NOT MEASURED (exit 75), never as a pass.
2026-09-29 01:38:08 +00:00
Doxa Maps
af8da2259c Any group set can generate packets from one folder
The generator could already serve a second group set -- series.json's
country series proves it -- but only one wired into series.json, reading
upg100's bucketed clusters, upg100's legend tree for enumeration, and the
shared records cache. A fork had no way in.

The contract is now one folder. DATA_DIR points at it; clusters.json with
a flat lists[] is the group set; records.json in the same folder means no
API and no cache; selector-evaluator.js resolves from there, as it always
did. normalize.js and legend-tree.js become optional -- absent, records
are used as-is and the flat file IS the enumeration -- and the absence is
logged rather than silent, because a silent fallback would let a typo'd
normalize.js skip normalization on the real series. PACKET_OUT keeps a
fork's output away from the real artifact directory, where toy packets
would be counted as coverage.

unlisted: true carries upg100's reachability idea across as data: the
list is defined and selectable with ONLY=, and not generated by default.
The bucketed shape keeps its legend-tree enumeration untouched, so the
deaf collapse and the 28-zone arithmetic are byte-for-byte unchanged.

The smoke test was written first and failed first, on all four counts.
Its fixture is synthetic -- every slug and number invented -- and it
asserts the unlisted list does NOT generate, which is as load-bearing as
the two that do: an unlisted entry that generates anyway is the
reachability bug inverted.

One bug found in my own work while proving it: allZones became async, and
the funnel's listCount read it from inside a try/catch that returns null
on error -- so a use-before-init did not crash, it silently deleted the
triangle from all 28 packets. Caught because a probe for the tip's "1 of
28" found nothing, not because anything failed. Resolved once, before
the funnel is built; regenerated and verified -- funnel present, tip
reads 1 of 28, 95 photos, 11 sheets.
2026-09-28 23:01:20 +00:00
doxa-maps-88655c70
04eccdfa7a Gate the scrim-covers-viewport rule, and say what was not verified
Some checks are pending
build-gate / build (push) Waiting to run
A `position:fixed; inset:0` scrim is only viewport-relative while nothing
establishes a containing block for fixed descendants. VerticalTabsLayout sets
`contain:layout` on .vtl-content, so inside the cap-5 studio every such scrim
covered the CONTENT PANE instead. Measured at a 1280x900 viewport:

    .dt-scrim / .modal-scrim / .lt-overlay ... 1024x793 @ (256,107)
    captured by div.vtl-content [contain:layout]

256 is the rail width, 107 the header height — left uncovered and CLICKABLE
behind what still looks like a modal. The fix (library/utils/viewportScrim.js)
measures the real origin and subtracts it, rather than removing the shell's
contain, which is load-bearing for a different reason.

This adds the gate that keeps it fixed. A source grep cannot: every one of these
scrims greps as a correct inset:0, because the variable is the ANCESTOR, not the
declaration — and which ancestors exist depends on the HOST, not the component.

check:scrim-viewport:self-test reverts each scrim to plain inset:0 and requires
EVERY case to fail; a gate that cannot fail is worse than no gate, and this one
measures a property that looks fine in a screenshot either way. The self-test
also surfaced that .lt-overlay is captured TWICE — div.lt-root [transform] as
well — collapsing it to 233x30 rather than merely to the pane.

SemanticTreeLegend's .stl-export-backdrop is fixed the same way but marked as
NOT verified through the UI: it sits behind SHOW_EXPORT_BUTTON = false, so the
menu that owns it cannot be opened. Correct-if-enabled, not observed.
2026-09-22 21:54:39 +00:00
doxa-maps-88655c70
762953a054 Land the swarm's dashboard work: colour, rails, cascade, scroll
Some checks are pending
build-gate / build (push) Waiting to run
Sixteen source files from five agents, all verified against source gates
before commit: template-literal, sfc-parse, kpi-unit, composite-reg,
facet-cascade, one-palette, script-flags, no-public-binds, no-orphan-gates and
quoted-scripts all pass. public-hygiene reports 0 findings.

Colour. The choropleth ramp is now derived by interpolating the product's own
tokens (surface #f3f3f1 to accent #92b195) rather than adding a sixth grey, and
that fixes a pincer the earlier research had not named: on the old grey-to-black
ramp the navy dot and the white ring failed at OPPOSITE ends, so no fixed pin
colour could win. Worst-case dot contrast moves 1.13 to 7.29. Second-order
breaks fixed with it -- the country outline would have measured 1.08-1.82 as
white on the pale fill, so it is #6d766e at 2.59-4.34, and the legend swatch
border.

Religion colours no longer key by array index. colorFor takes no index at all;
colour comes from the registry by facet plus value. All 31 distinct live
religion labels now match the map, 0 mismatches. Tree-rail grey rows 142 to 0
against the live 2,107-row API.

Cascade. Clearing a parent facet now clears its descendants, in the selection
bus rather than in any component, because every clear path already funnels
through setFilter/clearFilters -- a component fix would have fixed one button
and left four. The hierarchy is derived from config.facetRail[].drill, so it
cannot drift from the tiers the rail renders. Place turned out to be three deep
(region, block, country), not two.

Rails. Tree rows wrapping 18/66 to 3/66; the tree rail is now the wider of the
two at 296 vs 264, with the map cell unchanged because both pairs sum to 560.

Also lands the shell scroll fix and the facet-cascade gate with its committed
fixture -- 73 sampled rows, no network, so the assertion that reproduces the
empty map runs on a fresh clone instead of silently skipping.
2026-09-22 18:57:39 +00:00