Commit graph

3 commits

Author SHA1 Message Date
Doxa Maps
d3cdc2c3bc Two gates the branch backlog turned red
Seven Driver citations came back with the merged branches. Each keeps its
engineering reason and its date, and loses the speaker: the overlap report, the
poster's legend rule, the network-change defect, the chart's provenance.

check:plural-forms crashed rather than failed. It built its locale list by
listing the catalogue directory, so a README.md moved in beside the locale
folders became a 'locale', passed the exists check because a file does exist,
and crashed readdirSync on a non-directory. It now filters to directories. The
gate on development has the same line and only survives because that README is
not there yet -- so this is a fix for development, not a merge artefact.

Both found by running the full 103-gate chain; the shortened chain the merge
produced ran neither.
2026-09-29 14:55:35 +00:00
Doxa Maps
15fc38555c The language tree becomes anyone's, and the numbers nobody wrote down get written down
Driver brief §B. gospel-internet's GiTree had NO props and NO emits: it read a board
composable and three module singletons, which is what made it gospel's rather than
anybody's. Everything it reached for is now a prop, and the parts worth testing are no
longer inside an SFC a gate cannot run.

WHAT IS ACTUALLY IN THE COMPONENT: wiring, 160 lines. The drawing is the whiteboard
tiered-points layer, the surface is whiteboard-map.js (moved out of gospel's plugin --
67 lines whose every import already came from library, so it was never theirs), the
hierarchy is language-tree-model.js and the camera is frame-target.js. Four modules,
four headless gates. An SFC cannot be executed by a check, so it should hold as little
as possible.

THREE BEHAVIOURS PINNED BECAUSE THEY ARE EASY TO STATE WRONGLY:

FRAME ON SELECT, NOT ON CHANGE. Clearing does not re-frame; the camera stays where the
reader left it. "It frames when the selection changes" is the obvious phrasing and it is
wrong in exactly the case that annoys people -- being yanked somewhere after deselecting.

A SELECTION THAT ARRIVES BEFORE THE MAP IS REPLAYED. GiTree survived this by ordering:
its host set the node through a composable that waited for data, so the value always
landed after the map. As a PROP the ordering belongs to the host, and a prop whose effect
depends on WHEN you set it is a trap -- a shared deep link would work or not depending on
load timing, which nobody could report. ready replays whatever `selected` is.

HOVER CANNOT MOVE THE CAMERA, structurally rather than by convention: hover sets a filter
on its own layer and there is no camera call on that path to remove later.

AND THE CONSTANTS. gospel's maintainer inherited GiTree and had to read intent out of
structure -- there is no "this took measuring" note anywhere in it. Asked which line they
would flag, they named frame(). So padding 60 (the outermost nodes carry labels that
extend past the point), maxZoom 8 (a two-language family must not fill the screen with
its own neighbourhood gone), duration 900, and the floor zoom that stops framing one node
from zooming the reader OUT are now stated with their reasons and asserted.
Two branch rules with them, both of which a tidy-up would erase: reduced motion is a
DIFFERENT CALL (jumpTo, not easeTo with duration 0 -- the engines do not treat those as
one), and a single point CENTRES rather than fitting a degenerate box, because a
one-language family is ordinary and fitBounds on no extent zooms to nothing.

THE ROLLUP IS OVER THE READER'S SCOPE. "A family shows the worst colour below it" is only
true relative to what is currently on the board: a family that is red overall can be
green inside a filter. Handing the rollup a whole subtree makes the colours quietly
disagree with the board -- no error, no blank, just a wrong fact about a real people
group. Asserted in both directions, and colorFor stays injectable because nothing in the
drawing depends on the rollup being worst-of.

THE CLICK TOGGLE MOVED TO THE HOST, and that is a change rather than a codification:
GiTree cleared the selection when you re-clicked the selected node, and that toggle lived
in the component's own handler. A host that wants it must now write it. In exchange the
chart holds no selection state.

gospel's UI is not wired and their boardEngine.js is untouched on main; they adopt on
their own schedule.
2026-09-28 16:30:49 +00:00
Doxa Maps
03c310833b The whiteboard is a layer of tiered points; only the edges were ever a tree
Driver brief §B, the seam under the language tree, so gospel's set charts inherit zoom,
label priority and tiling from the map engine instead of re-implementing them in SVG.

READ FROM GiTree RATHER THAN ASSUMED: six layer groups, exactly ONE hierarchical. The
parent->child links are sourced from linkFC; the points, the selection and hover rings,
the dish behind a cluster and EVERY label layer come from one point collection and know
nothing about trees. gospel re-read that independently and found the same split, which
is the only reason I trust it -- "the seam is generic" is the claim that should never
travel on one reader.

So library/charts/whiteboard/tiered-points.js takes a point FeatureCollection whose
features carry id, label, tier and priority, and gives each TIER its own symbol layer
with its own minzoom. That is the whole of "band names zoomed out, ring names mid, group
names in": the engine is told which labels may exist at which zoom and resolves the
collisions itself. Paint expressions pass through unread -- the layer has no opinion
about what a colour means, which is what keeps it reusable. Selection and hover are
FILTERED layers, so hover costs one setFilter and structurally cannot move a camera:
there is nothing in here that could.

AND THE PROJECTION IS NOT A SCALE, which is the part that would have shipped wrong.
gospel asked for degrees and planned to scale by one number; their charts are CONCENTRIC
RINGS, and Mercator stretches vertically by 1/cos(lat) -- 1.0154x at 10 degrees, 1.1547x
at 30 -- so a linear scale renders rings as OVALS, worst at the outer ring where the
labels a reader compares are. library/maps/whiteboard-projection.js is the inverse
conversion, EXTRACTED from build-language-tree.mjs rather than written, because the
second copy was about to be made by hand. The degree-height it emits shrinks from 0.9996
of the width at the centre to 0.6794 at the edge; that shrink IS the correction the
renderer then cancels.

TWO TESTS, EACH GUARDING A DIFFERENT RISK.
check:whiteboard-projection reproduces the old private implementation VERBATIM as a
reference and compares over 1,734 grid points EXACTLY -- the language tree is a
build-time artifact, so a rounding shift would move 4,000 coordinates and look like
nothing. It then asserts a ring renders round at the inner, middle and outer radii, AND
that a linear scale FAILS that same assertion, because an assertion the defect can pass
is measuring nothing.
check:tiered-points runs gospel's CENTERED SET through the layer: 2,079 points, 3 tiers,
priority by population, and NO EDGES. The absent links layer is the evidence -- if the
whiteboard draws this chart with no line layer anywhere, edges are separable in fact
rather than in my reading. The map is a recording fake, so the assertions are about what
the module DOES; a regex over the source would pass on a module that never called
addLayer.

Negative-tested: making the projection linear, changing its rounding, dropping the label
priority, or giving every tier the same minzoom each turn one of them red.

gospel's UI is NOT wired -- their component is theirs to write, later, against a module
that now exists. Their boardEngine.js stays where it is until the chart lands.
2026-09-28 16:24:40 +00:00