From c73a0be9da3a9a212212ac6c84c46148afe70f93 Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Tue, 29 Sep 2026 03:20:06 +0000 Subject: [PATCH] Shipped code cited a document nobody outside this machine can open MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Seven comments across six files pointed at SPEC-GOSPEL-INTERNET-DASHBOARD, which lives only in _qa-cycle: never cloned, never published. A reader of the public source met a citation they could not follow, which is worse than no citation — it implies the reasoning is written down somewhere they may look for. The durable part is now plugin-designs/gospel-internet/README.md, written as BUILT rather than as specced: the views and their shared URL state, the list model and its fail-closed rule, the two set charts (rings, bands, the label keep-out, fit vs fill-all and why band depth is the binding constraint), why the charts fit their box, the data joins, the columns, and the gates that guard each of those. Section 13's honesty rules are carried over close to verbatim, because they are constraints on the product and not notes: pins are approximate, never "reached", never who works where, internet figures are the country's, public data only. Two citations did not become README links, deliberately. The facet rail's quoted a design INSTRUCTION ("comes from the kit; do not re-implement"), so the instruction is kept and only the unreachable document name dropped — inventing a README section to host it would be citing myself. board.meta.json's and the instances file's now point at the README section that actually describes the URL-state behaviour they depend on. The instances file is edited through the JSON parser: my first attempt did string surgery and put unescaped double quotes inside a string value, which JSON.parse caught before it could ship. --- plugin-designs/gospel-internet/README.md | 120 ++++++++++++++++++ .../gospel-internet/board.meta.json | 2 +- plugin-designs/gospel-internet/board.vue | 2 +- .../gospel-internet.instances.json | 2 +- .../scripts/build-gospel-internet.mjs | 2 +- plugin-designs/gospel-internet/src/columns.js | 2 +- .../src/components/GiDrawer.vue | 2 +- .../src/components/GiFacetRail.vue | 4 +- plugin-designs/gospel-internet/src/model.js | 2 +- 9 files changed, 129 insertions(+), 9 deletions(-) create mode 100644 plugin-designs/gospel-internet/README.md diff --git a/plugin-designs/gospel-internet/README.md b/plugin-designs/gospel-internet/README.md new file mode 100644 index 000000000..a6f7387df --- /dev/null +++ b/plugin-designs/gospel-internet/README.md @@ -0,0 +1,120 @@ +# Gospel × Internet + +Which people groups already have gospel resources in their own language, and where the internet can +carry those resources to the ones that do not. + +This file is the durable record of how the board is built. It replaces a planning document that lived +outside the repository, so the comments in this bundle can cite something a reader of the public +source can actually open. + +## The views + +One tab strip over one shared state. Every view is a URL state +(`?view=&access=&band=&country=&province=&lang=&list=&group=`), so any view can be shared or embedded +exactly as the reader left it. + +| View | What it answers | +|---|---| +| **Board** (default) | map + facets + grid + drawer + charts, over every group in the chosen list | +| **Nothing in own language** | the groups with no resource found, as a front door, by population against their country's access band | +| **India & China by province** | province outlines and per-province counts, where national figures are too coarse to act on | +| **Language tree** | every language family of these people groups, coloured by what exists in each language | +| **Bounded set** | one boundary: reached inside, unreached outside | +| **Centered set** | rings by how close a group is to a reached neighbour, arrows pointing inward | + +## The people-group list + +The board draws a **list**, not the whole table. The default list (`doxa`) fetches nothing and filters +nothing. A different list keeps only the rows whose slug belongs to it and **fails closed**: if its +membership cannot be loaded the map gets no rows rather than silently falling back to everything. + +## The two set charts + +Both are drawn on a **whiteboard projection** — an abstract plane mapped through the map engine, so the +charts pan, zoom and click like the rest of the board instead of being pictures beside it. + +**Rings.** 1–10 is placement by distance from a reached neighbour; ring 0 is the centre (the group *is* +reached, not missing); ring −1 is "no relation found", which is a real outcome and never rounded onto +ring 10. A group absent from the join is reported as unmatched, separately again. + +**Bands, not circles.** `library/components/charts/ringBands.js` spreads the groups on a ring *through* +its band rather than along one circle. Positions are a pure function of the group's own id, so two +renders — and two printings of one packet — are identical, and a dot that moves means the data moved. +There is deliberately no seed: a seed would rescramble every dot while the data was unchanged, which on +a printed sheet is unrecoverable. + +**Labels first.** Each ring label reserves an arc, and the band fill refuses to place a pin inside it. +Both come from one source, so "pins never overlap the labels" holds by construction. A label longer +than its ring can carry truncates rather than eating the circle: uncapped, a long name on ring 1 once +claimed 96% of that small circle and the innermost ring drew empty. + +### `fit` and `fill-all` + +The binding constraint is the band's **depth**, not the arc's length. Separation cannot fall below twice +the pin floor, so a band of the current depth holds at most three rows — and no separation setting +reaches the busiest ring's population. + +- **`fit`** (the default today) keeps every pin separable at the chart's own scale and *reports* what + will not fit. This is the honest mode for a printed sheet, which cannot zoom: a dot a reader cannot + resolve is a dot that is not there. +- **`fill-all`** places every group and lets zoom do the separating, since separation is in map units. + Zoomed out a busy band reads as an arc of colour; zoomed in the pins separate and become clickable. + +Whichever mode is used, the caption states the number of groups that **have a ring** — a fact about the +data — and never the number of pins currently drawn, which would change as the reader zooms. + +### Why the charts fit their box + +The engine renders one world without copies, so it will not zoom out past the point where that world +fills the canvas *width*. On a wide, short figure that floor is too close for the drawing's own height, +and the outermost groups fall outside the frame. The fix is the chart's own footprint +(`extentFraction`), not the engine's behaviour. `check:gospel-set-fit` asserts every layout still fits +at that floor, with the previous footprint as its negative control. + +The fit follows the container with a `ResizeObserver`: these panels are `v-show`, so a map can be built +while its container is `display:none` and zero-sized, and a one-shot fit against that box lands at the +wrong zoom. + +## Data joins + +People groups join the near-neighbour table by **slug** first — the only key that is one field on both +sides and unique by construction — then by `rop3 + iso3`, then by `name + iso3`. Rows that match none +of these are counted and reported, never guessed at. + +Administrative names are matched **diacritic-folded**, at the lookup rather than by spelling each +variant, so "Jämtland" and "Jamtland" reach the same zone. + +## Honesty, and what this board will not say + +These are constraints on the product, not preferences: + +- **Pins are approximate.** A dot marks where a people is found in a country, not a town. The notice is + shown on the map and in every drawer, and provinces are marked approximate everywhere. +- **Never "reached".** The words are "resource found in the sources checked" and "nothing found in own + language". No game words — nothing is "claimed" or "done". +- **Never who works where.** No engagement status, no agencies, no workers, no teams, no field access. + The column exists in the data and is not read. +- **Internet figures are the country's**, or the state's, with source and year on every surface. There + is no group-level internet claim. +- **Public data only**, and every outbound link goes through the doctrine screen and allowlist. + +## Columns and export + +One row per group: people group · country · province (IND/CHN, approximate) · language · population · +access level · best resource · bridge language and strength · country internet % with year · internet +band · top platform · pray link. Hidden by default but available in the picker and in CSV: +`translated_by`, `training_url`, `bridge_training_url`, `deaf_bridge_publisher`, `mobile_per_100`, +`people_praying`. + +Export writes the filtered grid in its visible columns, with a header line carrying the sources and the +date. + +## Its gates + +| Gate | What it refuses to let through | +|---|---| +| `check:gospel-set-charts` | the set layouts' pinned properties, including that no chart grew a `seed` prop | +| `check:gospel-set-fit` | a layout whose drawing cannot fit its canvas at the engine's zoom floor | +| `check:ring-bands` | a pin outside its band, two pins closer than their own diameter, a pin under a label, or output that is not identical for identical input | +| `check:gospel-zone-tree` | the admin→zone join, including the diacritic folding | +| `check:gospel-whiteboard` | the whiteboard projection's contract | diff --git a/plugin-designs/gospel-internet/board.meta.json b/plugin-designs/gospel-internet/board.meta.json index 7b81fde72..f92967a0f 100644 --- a/plugin-designs/gospel-internet/board.meta.json +++ b/plugin-designs/gospel-internet/board.meta.json @@ -5,7 +5,7 @@ "pinHint": false, "urlState": "query" }, - "note": "pinHint:false because the board has its own legend and drawer; the shared coach-mark would float over the facet rail. urlState:'query' makes the board's own page keep ?view=&access=&band=&country=&province=&language=&group= in the address bar (shareable views, SPEC-GOSPEL-INTERNET-DASHBOARD section 3). A partner embedding the element without it keeps their URL untouched: the board READS the query always and WRITES it only when asked.", + "note": "pinHint:false because the board has its own legend and drawer; the shared coach-mark would float over the facet rail. urlState:'query' makes the board's own page keep ?view=&access=&band=&country=&province=&language=&group= in the address bar (shareable views, README.md, \"The views\"). A partner embedding the element without it keeps their URL untouched: the board READS the query always and WRITES it only when asked.", "stage": "experimental", "heavy": true } diff --git a/plugin-designs/gospel-internet/board.vue b/plugin-designs/gospel-internet/board.vue index 8a4d6ddad..a59fff266 100644 --- a/plugin-designs/gospel-internet/board.vue +++ b/plugin-designs/gospel-internet/board.vue @@ -6,7 +6,7 @@ * * One board for sending gospel resources through the internet, embeddable by partners. * - * SPEC: _qa-cycle/03-deployment-cycle/SPEC-GOSPEL-INTERNET-DASHBOARD.md (MVP = section 15, phase 1). + * How this board is built, and what it refuses to say: ./README.md * * THE SHELL, built on the dashboard kit: the selection bus (library useSelectionBus, filter / selection / * hover), the layout engine (library useDashboardLayout: three drag handles, keyboard operable, diff --git a/plugin-designs/gospel-internet/gospel-internet.instances.json b/plugin-designs/gospel-internet/gospel-internet.instances.json index 31f784c56..50b48c7d8 100644 --- a/plugin-designs/gospel-internet/gospel-internet.instances.json +++ b/plugin-designs/gospel-internet/gospel-internet.instances.json @@ -1,5 +1,5 @@ { - "_comment": "Preset partner embeds of (SPEC-GOSPEL-INTERNET-DASHBOARD section 14). Keyless: provider 'auto' falls to MapLibre with no token. Each preset is a URL a partner can QR.", + "_comment": "Preset partner embeds of (see README.md: every view is a URL state, so a preset is just that URL). Keyless: provider 'auto' falls to MapLibre with no token. Each preset is a URL a partner can QR.", "instances": [ { "id": "board", diff --git a/plugin-designs/gospel-internet/scripts/build-gospel-internet.mjs b/plugin-designs/gospel-internet/scripts/build-gospel-internet.mjs index fb6b916eb..274fe10e3 100644 --- a/plugin-designs/gospel-internet/scripts/build-gospel-internet.mjs +++ b/plugin-designs/gospel-internet/scripts/build-gospel-internet.mjs @@ -27,7 +27,7 @@ * (It replaces this builder's own point-in-polygon, which agreed with theirs group for group.) * * ★ WHAT IS NEVER READ: doxa-people-groups.rows.engagement, or anything that says who works where - * (SPEC-GOSPEL-INTERNET-DASHBOARD section 13). Nothing from plugin-designs/key-cities/. + * (see ../README.md, "Honesty"). Nothing from plugin-designs/key-cities/. * * Codes, not labels, cross into the plugin: access_level own|bridge|zero, access_detail * read_watch|hear|read|stories|none, internet_band yes|partial|no|nodata. The plugin translates. diff --git a/plugin-designs/gospel-internet/src/columns.js b/plugin-designs/gospel-internet/src/columns.js index 07c13a501..b56023ea1 100644 --- a/plugin-designs/gospel-internet/src/columns.js +++ b/plugin-designs/gospel-internet/src/columns.js @@ -1,6 +1,6 @@ /** * columns.js — the grid's columns and the CSV's columns: ONE list, so what a reader sees is what the file - * holds (SPEC-GOSPEL-INTERNET-DASHBOARD section 5 and 9). `hidden` columns are off in the grid until the + * holds (see ../README.md, "Columns and export"). `hidden` columns are off in the grid until the * picker turns them on, and the CSV exports exactly the columns that are ON. * * `csv` is the raw value (codes, numbers, URLs) — a spreadsheet joins on it, so it is never translated. diff --git a/plugin-designs/gospel-internet/src/components/GiDrawer.vue b/plugin-designs/gospel-internet/src/components/GiDrawer.vue index 05e5aabfe..7bdabb092 100644 --- a/plugin-designs/gospel-internet/src/components/GiDrawer.vue +++ b/plugin-designs/gospel-internet/src/components/GiDrawer.vue @@ -1,6 +1,6 @@