Project 7000, 4700, 2000 and 700 each need their own clusters of about
100 groups, and the next one should be built by following a pattern
rather than by copying this plugin.
The document separates two halves that turn out to be independent:
which GROUPS are in the list (library/lists/, shared, already built and
gated by check-list-prop-unit) and which ZONES they are cut into (this
plugin's data/clusters.json). A new list needs the second half only if
it wants its own territories.
Measured rather than described:
28 leaf zones from 29 clusters.json entries -- the two deaf buckets
collapse into one leaf. Never count zones from the buckets; ask
leafZones(buildLegendNodes(...)), which is the one answer poster,
packet and legend all agree on. Counting by hand produced 27 once
(forgetting deaf) and a delete of 30 files another time.
5 registry lists, all present, with the membership counts they
actually have (unreached 2082, unengaged 1929, frontier 1745,
frontier-500k 71). doxa has no membership file, correctly: it is the
unfiltered default.
7 selector types, and which type a zone uses IS the statement of how
it was zoned -- from wagf_block (pure geography) through to an
explicit pg_slugs roster. The 12 pg_slugs zones are the honest case,
not the lazy one: below country level there is no ADM1 field to select
by, so a rule would be a fiction that looks principled.
The deaf poster stays recorded as the known exception. It is global and
non-geographic by construction, the Driver has not solved it and said
so, and the pattern says that rather than inventing a zoning.
check-list-pattern-doc.mjs re-derives every number from clusters.json,
lists.json and the membership files and compares them to the document's
own text. Neither side is hardcoded twice, so it fails in both
directions: a doc that drifts, and data that moves while the doc sits
still.
Shown capable of failing, five ways: leaf count 28->27, a membership
count, a selector count, and a real new zone (which trips three claims
at once). The fifth is the useful one -- malformed data now exits 75,
NOT MEASURED, because an uncaught throw exits 1 and would read exactly
like the document being wrong. Found by mutation-testing; the first
draft crashed with a stack trace I would have mistaken for a pass at
catching drift.
Also fixed in the harness: the registry-id parser matched any backticked
lowercase table cell and read the selector table's `union` as an unknown
list -- a false failure against a correct doc. Each table is now parsed
from its own section.