THE DRIVER RULED ON THE PALETTE. He compared the two screens himself: "when i
look on teh color ffor th regiosn tab of the simpel research map i don tsee the
you using htes colors on the reigosn dashbarod hwen you shoudl". Two surfaces
disagreeing about what Africa's colour means is a defect whichever palette is
prettier, and he chose which one wins. library/colors/wagf-region.js now
carries doxa-research-map/src/colors/doxa-region.js's values.
The blast radius turned out to be nil, and my earlier escalation overstated it:
wagf-region.js is imported by exactly two files, both mine. I had reported it as
needing a cross-agent decision on the grounds that seven surfaces would be
repainted. They would not have been — nobody else imports it.
WHAT THE CHANGE COST, measured with the same validator and surface the previous
palette was stepped against:
lightness band FAIL #1ee05f .793 · #FFEB3B .928 · #00e0bd .810 sit
above the .43–.77 band
chroma floor PASS all 7 >= .1
CVD separation PASS worst adjacent #FFEB3B/#1ee05f dE 8.7 protan
normal vision PASS worst adjacent dE 21.0
contrast WARN under 3:1 vs surface: #1ee05f 1.72 · #FFEB3B 1.19
· #00e0bd 1.65 · #ff7f11 2.47
The palette this replaced passed all five. The contrast warning is survivable
here for a reason and not by dismissal: it obligates relief through a secondary
encoding, and both surfaces already carry one — the chooser renders a legend of
labelled chips in these colours, the dashboard renders the same seven as a
table. Identity never rests on hue alone. The lightness failure has no such
relief: three of these read faintly as pins on a light basemap. Darkening those
three by about 10% L would enter the band with the hue still recognisable, and
that is the Driver's call, so it is asked rather than done quietly.
_chk-region-palette-agrees.mjs is what stops the two drifting apart again. It
compares them value by value and names both files on a disagreement. Ours is
imported; theirs is parsed, because doxa-region.js imports a Vite alias and node
refuses the module — so the parse asserts its own shape first, requiring the
eight entries that file declares, and fails rather than comparing against an
empty object if anyone refactors it to computed values. Negative-tested: flip
one hex and it names that region and both paths.
AND ONE UNIT IN THIS PORT WAS NOT PX. The chevron's gap was a literal 1rem,
ported from their `.with-icon .icon.right { margin-inline-start: 1rem }` — 10px
at their 10px root. `rem` resolves against the document root wherever the
element lives, which is the whole reason dxlife-tokens.js resolves their scale
to px, so ours rendered 16px on our own leaf and would render 10px by accident
on a host with the common 62.5% reset. Pinned to 10px. No diff caught it: the
list diff's link spec reads colour, decoration, weight, display and alignItems,
and never a margin. It reads the icon's margin now.
|
||
|---|---|---|
| .claude | ||
| .forgejo/workflows | ||
| .github/workflows | ||
| _archive/staging-surface-2026-09-22 | ||
| data | ||
| deployment | ||
| documentation | ||
| internal | ||
| library | ||
| plugin-designs | ||
| public | ||
| serving | ||
| skills/make-a-doxa-map | ||
| .env.example | ||
| .gitattributes | ||
| .gitignore | ||
| AGENT-BRIEF.md | ||
| bun.lock | ||
| capacitor.config.json | ||
| LICENSE | ||
| package.json | ||
| README.md | ||
| vite.config.js | ||
| vite.doxa-maps-build-config.json | ||
dx-maps — one build, maps that embed everywhere
Write a map once in plugin-designs/<your-map>/, run one build, and drop it on any site —
iframe, script embed, mobile app — or open every map at once as one app. Five plugin
capabilities, five deployment methods, one shared library, one build.
| What dx-maps set out to do | Proof |
|---|---|
| One build → one folder you can drop on any host | bun run check:drop |
| Every map embeddable from a copy-paste snippet | deployment methods · bun run check:embed-targets |
| Every map interoperable through props and events | bun run verify:interop |
| One app nests every map — and each stays embeddable on its own | capability 5 · bun run check:composite-reg |
| 5 capabilities × 5 deployment methods | bun run verify:5-caps |
| Shared library, private plugins — reuse without breaking each other | bun run check:library-index |
Many agents, one green main |
agent swarm |
The 5 plugin capabilities
A capability is how much a plugin nests, from one element (1) to an app of apps (5). Every screenshot is a real bundle from this repo's build.
Capability 5 — app shell: the Research Gallery
One plugin, one element (<doxa-maps-spa>), and every tab inside it is a real plugin from a lower
tier that you can still embed on its own. plugin-designs/doxa-maps-spa/
What the Research Gallery is made of — all five capabilities in one plugin
Research Gallery <doxa-maps-spa> capability 5
├─ Dashboards
│ ├─ Big Picture dashboard-plus-grid capability 4
│ │ ├─ research dashboard research-dashboard capability 3
│ │ └─ people-groups grid people-groups-grid capability 2
│ ├─ Simple Research research-map-plus-grid capability 4
│ │ ├─ research map doxa-research-map capability 3
│ │ └─ people-groups grid people-groups-grid capability 2
│ └─ Regions regions-mfe capability 4
│ ├─ regions list regions-list capability 1
│ └─ region dashboards region-dashboards capability 1
├─ Prayer Mobilization
│ ├─ UPG-100 List upg-100-mfe capability 4
│ │ ├─ list upg-100-list capability 1
│ │ └─ poster wall poster-wall capability 1
│ └─ Prayer Progress doxa-simple-map capability 2
├─ Gospel Research
│ ├─ Cities with Internet connectivity-map capability 1
│ └─ Gospel Resources resources-map capability 1
└─ Experimental every other bundle, discovered automatically
Capability 4 — composite: plugins wired together
Nests lower-tier plugins and wires them with props and events. plugin-designs/my-upg-100-list/
| List (cap 1, embeddable alone) | Poster wall (cap 1, embeddable alone) |
|---|---|
![]() |
![]() |
Capability 3 — multi-view: many views, one element
plugin-designs/doxa-research-map/
Capability 2 — presets: one plugin, many maps
One root.vue plus a preset table. plugin-designs/doxa-simple-map/
| Prayer | Engagement | Adoption |
|---|---|---|
![]() |
![]() |
![]() |
Capability 1 — single element
One folder, one .vue, one element. plugin-designs/all-people-groups/
Which bundle sits at which tier is generated, not remembered: documentation/reference-capability-exemplars.md.
The 5 deployment methods
1. iframe — the prayer map on any site ✅ production
One <iframe> tag, no build step. Copy the embed.iframe snippet from manifest.json.
2. Mobile app — Simple Research on a phone ✅ built
The Simple Research dashboard (research map + people-groups grid), shipped on its own as a
Capacitor app. bun run check:mobile · bun run phone (needs Android SDK + JDK 17)

3. JavaScript embed — the Research Gallery on dx-life ✅ production
The whole capability-5 app on the dx-life research page, from one <script src> and one element —
replacing the separate research map, list and regions dashboards the page used to carry. Copy the
embed.script snippet (SRI-pinned) from manifest.json.
4. WordPress / Disciple Tools plugin — 🧪 proof of concept
A shortcode + block plugin in deployment/wordpress/; its contract test passes
(bun run check:wp-embed). Proven, not production-ready.
5. Desktop app (Tauri) — 🧪 proof of concept
A desktop binary and .deb build from deployment/src-tauri/ (bun run check:tauri). Proven,
not production-ready.
Mapbox token: the dev server bakes one in; built pages take
?tk=pk.…, a pasted token, or a token endpoint. Or skip Mapbox entirely:provider: "maplibre"on any instance renders on MapLibre with no key. Every command and prerequisite: documentation/README-SECTION-3-cli-commands.md.
Build it with an agent swarm
Trunk-based development with a merge queue — the model behind GitHub's merge queue and GitLab's
merge trains. Many agents build in parallel, each in its own worktree; changes land on main one
at a time, rebased and gated, so main stays green.
# one worktree + one agent per plugin
git worktree add -b regions/work ../dx-maps-regions main
git worktree add -b upg100/work ../dx-maps-upg100 main
# one dev server per agent, each on its own port
bun x vite --port 5301 --strictPort
# land through the queue: one at a time, rebased, gated
flock /tmp/dx-maps-main.lock sh -c '
git fetch origin && git rebase origin/main &&
bun run gates:pre && git push origin HEAD:main'
One agent owns the full build (it wipes the shared output first); everyone else checks their own
bundle with BUNDLE=<name> bun x vite build. A maintainer reviews what lands and keeps shared
code shared. Agents joining this repo: read AGENT-BRIEF.md.
Diagram source: delegation-model.svg.
The Plugin Studio — ★ where you work
Every plugin in one place, sorted by capability. Pick any plugin to run it live, see what it is composed of, and copy its embed code. Tell your agent which plugins to group, and it composes them into a higher-capability plugin with props, events and a layout — that is how the Research Gallery was built. (The studio is being redesigned; this screenshot tracks it.)
Get it running
You need git, Bun and Node ≥ 18.
git clone https://git.nuxtbase.app/doxa-life/doxa-maps.git
cd doxa-maps
bun install --frozen-lockfile
bun run dev # → http://localhost:5173/ is the Plugin Studio
No build needed first — the built tree is committed. To start a plugin, copy
plugin-designs/template-bundle/ to plugin-designs/<your-map>/; the studio finds it automatically.
| Command | What it does |
|---|---|
bun run dev |
The Plugin Studio, hot-reloading, at http://localhost:5173 |
BUNDLE=<name> bun x vite build |
Build one bundle only |
BUILD_ALLOW=1 bun run build |
The full deployable tree in public/doxa-maps/ (~4 min; refuses without the flag, to protect a shared tree) |
bun run serve |
Serve the built tree at http://localhost:4173 |
bun run check:settled |
Exit 0 = the built tree is safe to measure right now |
bun run gates:pre · bun run gates:post |
The verification gates |
bun run shots |
Regenerate these screenshots (ONLY=<name> for one) |
Repository layout
plugin-designs/ YOUR maps — one folder per plugin, a free zone
library/ the SHARED toolkit (layouts, components, composables) — a high bar
public/doxa-maps/ the build output: the drop-anywhere tree + manifest.json
deployment/ Capacitor · WordPress · Tauri
internal/ the build generator and the gates
documentation/ tutorial · how-to · reference · architecture — start at documentation/README.md
Maps should be open source, run everywhere, and multiply. One build makes each map a self-contained file for your site, your partners' sites and a phone. The Plugin Studio is where you compose them into something bigger.












