Output 3 opens with three sentences (browser to desktop with the same code, Rudder as the working Linux proof, its private-by-design goal) and one screenshot. How it works, Rudder's goal in full, and what isn't run yet move into a details fold-out. The two Studio bullets go back to one-line gists.
Under the desktop output, Rudder's goal from the start: offline, with no telemetry and no cloud. Your
own code and your own self-hosted speech model (NeMo-Speech.cpp and a ~742 MB model) on a modest
amount of CPU; after a one-time download of the runtime and model nothing needs the network (the
runtime was traced: zero sockets, zero DNS). Working out of the box is the aim; the first-run download
is still manual. Because a voice-to-text tool hears everything, changes to rudder-voice and
rudder-engine should come only from trusted people, each read by a second person. Planned: an egress
monitor that shows Rudder sends no data out.
The desktop output now leads with what it shows: what you run on the development server in your
browser ships as a desktop app from the same code, wrapped by Tauri in a small Rust program. Rudder is
the proof: the offline voice-to-text plugin runs in the Plugin Studio and as its own desktop app, and
the Linux app works and is used to dictate the work on many of these maps. The summary table, the
"speak instead of typing" and "one plugin, several outputs" bullets and the desktop section say so;
the details keep what is not run yet (macOS, Windows, the automatic production build).
The desktop HOWTO's proven table records the launched Linux app.
The product now names the plugin and its tab "UPG100 Posters" (its parts "Poster Wall" and "Zoned Map")
and keeps "UPG100 List" for the series. Where the docs mean the plugin (the gallery, the capability and
audience tables, Posterverse's tabs, the print steps, the screenshots' alt text, the layout tree, the
mobile-app list, the profiles and the diagram's bubble), they now say UPG100 Posters; the strategic list,
the poster-generation title and SERIES_NAME keep UPG100 List. The tree's list leaf is the zoned map, as in
the product. The diagram PNG is re-rendered at 2x.
The README and documentation call the series what the product now calls it: "UPG100 List", one term,
no hyphen. 28 prose mentions of "UPG-100", "UPG 100 List" and "List of 100 UPG's" change across the
README (19 lines, including the print section and the layout tree, whose columns stay aligned), four
documentation files and the delegation diagram's plugin label (PNG re-rendered at 2x). Paths, ids and
identifiers stay as they are (my-upg-100-list, upg-100-mfe, the image file names); the one code value
named, SERIES_NAME's default, follows series.json's seriesName "UPG100 List".
India's whole-country poster is withheld for now, so it leaves the needs-a-computer list and step 1 says
its state posters print. The poster walls print Letter and offer no size choice, so the large-size
paragraph names the two plugins that do (Bible Posters and the Wall of People Groups, up to 5 x 8 ft) and
describes only the glued print that ships today: the Wall's 5 x 8 ft poster as 3 sheets, one per page,
with a 6 in overlap, numbered and marked "glue here".
A short section after "On a phone" for readers: open a poster wall (UPG-100 List, Country Posters,
Regions), pick Poster Map, Prayer List or Card Deck under Print Options, and the browser's print window
opens as the preview, with Save as PDF to keep a file. It says which page and card sizes print, that
the large sizes (18 x 24 in and up) go to a print shop as a PDF with one page per sheet, that 66 x 66 in
and 5 x 8 ft print as 2 and 3 sheets glued with a 6 in overlap, and what a phone can print, including
the few prints that need a computer. The labels and sizes are the ones the code uses.
A change joins the pilot; only the landing branch is landed on. The swarm section and the diagram used
"land" for both, so the harness paragraph, step 5, the kitchen list, the alt text and the diagram's
centre, panel title and second card now say "join". The lock "works like a merge queue": it lets one
change through at a time, while the order comes from the coordinator. The kitchen list says "landing
server", and the glossary says main moves only when a person approves publishing development. The PNG
is re-rendered at 2x from the SVG source.
The swarm section now says what each branch is for: the landing branch ships and moves only when a
person approves a publish; the pilot branch is where reviewed PR branches come together; each plugin
or feature has its own PR branch. A new paragraph says how it scales (many people, each with several
agents, on many plugins: one PR branch each, seen on the pilot server once it joins). Step 5 names the
lock as a merge queue. Publishing to a public host happens only from the landing branch, when it is
ready, and a self-hosted git service is recommended because an agent can publish by accident.
The diagram gets the same: "a person approves" for the landing, "landing server", and a bottom band
for the scale and publishing rules. The PNG is re-rendered at 2x from the SVG source.
The lock guards the pilot, so it is the pilot lock, and the act is a merge onto the pilot: the
README's summary row, the harness paragraph, the kitchen list, the glossary and the alt text, and the
diagram's title, the lock ring, the coordinator line and the legend. "Publish to landing" and
"landing (main)" keep the word for the landing branch. Also: about 15 seconds to see a landed change;
a "merge" glossary row instead of "rebase", with pilot and landing beside development and main; the
cloud paragraph names BRANCH=development and PRODUCTION_BRANCH=main. The PNG is re-rendered at 2x.
Step 5 merged the pilot INTO the PR branch, which made the PR branch carry everyone's unpublished
work. It now merges the PR branch into a detached copy of the pilot inside the lock, checks, pushes
that merge, and switches back; a conflict is aborted and fixed on the PR branch. Step 4 says the
per-agent preview servers run on your own machine, and the cloud paragraph says to set the container's
BRANCH to development for the pilot server (its default is main).
The pilot branch (development) is the shared working version a hot-reload server follows; the
landing branch (main) is production, which its server rebuilds from; PR branches hold one feature
each, start from the landing branch and land on the pilot after the checks. The worktree, lock,
gates and build-owner steps stay. A short paragraph on cloud development with a swarm: a self-hosted
forge, a pilot and a landing server per app, and the pilot server's PR switching named as planned
only. The diagram's labels follow (pilot at the centre behind the lock, landing below); the PNG is
re-rendered at 2x from the SVG.
Copied unchanged from the bridge kit (727fb55): TUTORIAL.md (for the repository's GitHub admin,
about 10 minutes), pull-contributor.yml (the workflow it sets up; it refuses a contributor's
changes to .github/) and test-pull-contributor.sh (the TUTORIAL's "check the guard yourself",
which sits next to it). None of them is under .github/, so nothing runs by itself here.
README: one line in the agent section links the tutorial; nothing else in README changes.
The 34 state posters add a 560 px card thumbnail and a 1500 px download each (68 files, about 5 MB), which takes
public/dx-maps past the README's stated size by more than check:readme-size's 10% margin. The row now states the
measured size, as the gate prescribes.
Four lines after the Bottom line up front: these maps and posters are given to bless the
people who bless the nations; we love them, thank them for their work and bless them in
Jesus' name. Every poster is in Posterverse (the UPG 100 List, Country Posters, Poster
Design and Wall of People Groups) and in the Plugin Studio.
Only "## Build it with an agent swarm" changes.
- Bring your own harness: dx-maps works with whatever agent or harness you already use. The
repo gives the branch strategy, the checks (bun run gates:pre) and the Plugin Studio; it
ships no harness. A coordinator or a gate-keeper is a job your harness gives its agents.
- "Set up the branch strategy", seven steps: main is production; create development once;
a branch and worktree per agent (with bun install); a preview port per agent; land one at
a time through a lock (rebase, gates:pre, push only if nothing fails, since gates:pre
exits non-zero on a failure); one full-build owner and BUNDLE=<name> for the rest; release
by fast-forwarding main to a checked commit, then tag it and deploy from the tag.
- The plain-words kitchen list and the glossary stay, in line with the steps (the glossary
gains "tag"). Every command block parses, and every script it names exists.
deploy5-desktop-rudder.png is replaced with a screenshot of Rudder running (Ready, with a
transcript). It carries no names, emails, paths, hostnames or tokens, and its metadata is
stripped: the PNG holds only IHDR, IDAT and IEND. The alt text says what it shows.
delegation-model.svg, re-rendered to delegation-model.png at 2x (only IHDR, IDAT, IEND):
- development, not main, is at the centre, behind a landing lock that takes one landing at a
time; the plugin ring and the three outputs are kept (the desktop output working).
- A coordinator and a gate-keeper replace the single overseer; the legend gains them.
- "How a change lands" has four steps: own branch; a second reader; one at a time (in the
lock: merge development, rebuild, run every check, push only if nothing fails); promote
to main (only when the owner approves, after full checks on that version).
- The outputs band adds: the dev preview follows development, the published site follows
main.
README, the same section, so the text matches the picture: the summary table row, the
section's intro, the image's alt text, the kitchen list (now five points) and the glossary
("landing lock" replaces "merge queue"). No tool or personal names.
- The clone examples in README.md and documentation/DEPLOY.md name a private server; they
now read `git clone --depth 1 <your-repo-url>`.
- skills/plugin-designer/references/backend.md quoted a planning heading that names a
platform; it now says the tiers are recorded "as an open, unproven assumption".
- The desktop app (Tauri; Rudder runs as one) works: the capability table's Desktop column
is ✅, and the outputs table, the Output 3 heading and its screenshot's alt text say so.
The WordPress column keeps 🧪, so the table's legend is unchanged.
- delegation-model.svg: the desktop output's label reads WORKING, in the green of the other
two outputs. delegation-model.png is re-rendered from it at 2x in Chromium. Rendering the
previous SVG the same way reproduced the committed PNG with 0 pixels different, and the
new render differs only inside the desktop box. The PNG carries only IHDR, IDAT and IEND.
main gained one commit, 4a0fe87d0 (README.md only), so development no longer contained it
and the promotion could not fast-forward. The edit meets development's rewritten BLUF, so
README.md conflicted. Resolved as development's README with the same edit applied:
- "- **Why:** To Bless the Nations" is the section's first bullet, then a blank line, as
in main;
- development's existing Why paragraph is relabelled "- **Great Commission:**", its text
and line breaks unchanged.
Nothing else changes: README.md is the only file that differs from development.
From dx-maps' measurement on development 17d1a07cf; docs only.
Required, because the README contradicted the final page layout (/research shows the research map by
itself, with a link to /deep-research; /deep-research holds the gallery):
- The drop-in paragraph said DX Life's /research is filled with the Research Gallery. It now says the
drop-in copies the folder into public/dx-maps, and /research shows the research map with a link to
/deep-research, which holds the gallery as Deep Research.
- The JavaScript-embed section:
- heading, lead and "one embed replaces the research map" now describe /deep-research;
- the screenshot shows an earlier build (the gallery on DX Life's research page, the tabs in the old
order), so its alt text and a caption now say so. A reshoot is rega's.
Also, each a one-line factual fix (the README's structure is unchanged):
- The name, once where the gallery is introduced: the Research Gallery, which DX Life publishes as Deep
Research. The other 22 mentions and the headings, whose anchors other text links to, are unchanged.
- 7 vs 8 production maps: the eighth is the country map, for DX Life's country pages, not in the gallery.
- development is where work lands, and main moves only on a publish: the goals table, the swarm
section's two sentences, and the glossary.
- Both production grids now follow the gallery's section order (Prayer Progress before UPG-100 List).
- The embed heading's status reads "✅ works today", the README's own legend for ✅, instead of "production":
the page is not live on DX Life's production site yet (dx-maps' pre-read).
check:readme-size, check:doc-scripts, check:no-new-driver-references and check:link-coverage pass.
From exp's second read of 4bcce8ff8. "BUNDLE=<map> bun x vite build && bun run fix:sri" is right for a
code change, but a change to the map's .meta.json reaches its pages and the manifest only through
bun run tree. Without it the leaf page ships the old settings, and the landing scan's step 2b refuses
the stale tree. The recipe is now "BUNDLE=<map> bun x vite build && bun run tree && bun run fix:sri".
The tree step takes a few seconds and changes nothing when nothing changed.
Found re-reading 10be43d2b:
- "BUNDLE=<map> bun x vite build" alone leaves the map's pages and the manifest pinned to the OLD hash,
so the map's own page and any pasted snippet refuse the new file. It is now followed by bun run fix:sri.
Measured two-sided: one byte added to a pinned map's built file made check:sri FAIL (4 of its pages
disagreed); fix:sri rewrote them and the manifest, and check:sri PASSED. The probe files were restored.
A one-map build rewrote only that map's .js, never the manifest or a page.
- "A snippet is two parts" was wrong. The snippet also preloads the map engine and sets
window.MAP_APP_API_URL, so the README now says to paste it whole and names what it does.
- The agent table:
- Cities with Internet works by country (internet use), so it moved from "Reach a city" to
"Plan outreach". The production maps work by country and region; city-level planning (Prayer Walk
Planner) is experimental, and the row says so.
- Maps carry the names the app shows (Simple Research, UPG 100 List).
- "Frozen" was a tonight-only rule; the line now reads "leave them as they are unless your task is one
of them".
For vibe coders: run the Plugin Studio, build one map or all of them (and the automatic copy into a sibling
DX Life site), and embed a map: every map's own embed.script in manifest.json, loaded from DX Life's
public/dx-maps. For agents: the repo builds mobilization maps, and each goal (reach a city, mobilize for
people groups, plan outreach, prayer packets and resources) is mapped to the production maps that serve
it, with the skill, the plugin rules, the landing rules and the freeze on experimental maps.
A fresh reader given only the README and the HOWTO reached the Studio and a plugin's /__leaf page with
nothing else needed, but one step. After "port 8090 is already in use ... set DX_PB_PORT", neither
doc said the setting is read when bun run dev starts, so the new port only applies after a restart.
The README copies .env.example before starting the server, and the port message appears only once it
runs, so the steps looped with no stated way out. All three places that mention DX_PB_PORT now say to
pick a free port and restart.
A fresh reader given only the README and HOWTO-join-the-table got through install, bun run dev and
the Studio. One gap: no address for a single plugin's page. The Capability 1 fold says each plugin's
folder holds "an index.html for previewing it", which reads as /plugin-designs/<plugin>/index.html,
and that 404s under bun run dev. The page is /__leaf/<plugin>/<profile>, and the Studio's list is
/__plugin-gallery/manifest.json. Both are now named.
Three smaller notes from the same reader:
- the README copied .env.example to .env while the HOWTO uses .env.local. Both work and .env.local
wins; the README now uses .env.local too, which also keeps .env free for compose.yaml;
- the Mapbox token read as required; it is one of two ways to get a background;
- the 8090 port hint (DX_PB_PORT) was only in the HOWTO; it is now named where the README first
mentions PocketBase.
Also, the engine rule (was a follow-up commit, f0e965e4d):
Since 2026-09-28 useMapProvider.js resolves `auto` to MapLibre, and Mapbox is an explicit choice:
"provider": "mapbox" plus a token. The docs still said "Mapbox when a token is available":
- README's map-engines row;
- how-to.md's provider section;
- the plugin-designer skill's props table, which even says to confirm in useMapProvider.js.
All three now state the current rule. So do four lines that told readers to set provider maplibre to
avoid needing a key: README's Get it running and its "No token" fold, .env.example's token comment,
and reference.md's no-key row.
Case-insensitive git grep "Mapbox when a token" over README.md, documentation/ and skills/: 3 before, 0 after.
A clean clone of development, run with no settings and only the README:
- The drop-in's `bun run build && bun run dropin` stopped at the build, which refuses without
BUILD_ALLOW=1, so dropin never ran. The built maps are in the repo: the route is now
`EMBED_BASE_URL=https://your-site bun run tree` then `bun run dropin`, with the one-bundle rebuild for
changed plugins. Without the tree step the manifests' snippets stay relative and no other site can paste
them (measured: embedBase "." before, the site's address after, and the snippet's page answered 200).
- smoke:studio checks :5173 by default; when Vite took another port it measured a different server.
The step now passes the printed URL, and says it drives a headless browser.
- `bun run serve` crashes if 4173 is taken; PORT=<n> works and is now named.
- Vite prints http://127.0.0.1:5173/, not localhost.
- The sync example's branch must exist on the remote; the public repo has only main.
- bun run dev also downloads and starts a local PocketBase on 8090; now said. A copied template keeps
its element name until renamed as its README says.
"Where the folder goes" had two routes. The third is the same container with ACCESS=public on a
team's research subdomain: the Plugin Studio open to everyone (following development), its /cdn
built from main serving the site's production maps, and the admin parts behind a password set at
deploy. A small table ranks the three by capability: drop-in (the simplest, no backend),
subdomain (PocketBase behind a sign-in), Plugin Studio (the highest: plugins built live in
public). The two links to the section follow its new name.
The link to deployment/cloud's "Public Studio (ACCESS=public)" section resolves once
repo/public-studio-mode lands, so this lands after it.
The gate measures the built folder, so it belongs after the build, in
gates:post, with its description beside the others. It now reads the tree
name from _tree-name.mjs like every other tool: the folder was renamed
once already, and a gate hardcoding the old name kept reading it.
The row now says 1205 files, 154 MiB: the folder as it stands with the
350 city and country posters this landing ships.
dx-base measured two defects in the A8 draft for text that goes public:
- Links into the private DX Life repo answer 404 to anyone without an account. The live
example is now plain words (README, DEPLOY), and the clone line points at the public repo
(doxa-open/doxa-maps, as DEPLOY already did). DEPLOY's "three ways" becomes the two routes;
the CDN route is retired.
- The web folder row said 536 files, 76 MiB. The folder is 855 files, 119.2 MiB. New
check:readme-size re-measures public/dx-maps and fails when the row is more than 10% off,
printing the line to paste. It passes at 855/119; the old numbers fail.
- Bottom line up front names the persona dx-maps is for, vibe coders, and links to what
they get.
- "Cloud development environment": by default bun run dev is fully local. DX_SYNC_REMOTE and
DX_SYNC_BRANCH in .env.local make it follow a shared branch. It fast-forwards only a clean
checkout on that branch, and never merges over local work. If your own agents also edit
the checkout, it stops following, so push small and often or the landings pile into a merge
conflict.
- The drop-in route's live example pointed at a branch that doesn't exist (research-gallery);
it is research-gallery-drop-in, proven with every production map passing its embed checks.
- One line on SRI where hosts copy snippets: the integrity hash makes a changed bundle
refuse to run, so copy the snippet again after each release.
The deployment section named four hosting options, three of them CDN-shaped (Cloudflare Pages,
jsDelivr, Netlify). dx-maps now ships two routes, and the section says so in that order:
- The easiest path: drop-in. The folder goes into your own site's public/ and your site's server serves
every map; bun run build && bun run dropin. One server, no PocketBase. It is the route the DX Life
branches were proven on.
- Subdomain: the dx-maps container with PocketBase, for maps that need a backend of their own; the
Dockerfile and compose.yaml at the repo root.
The notes that still hold stay (relative links, iframes, the one CORS header for JS embeds). The
quick start's own drop-in steps become a pointer to the route, and the cloud dev server is described
as following development, not main.
The drop-in was proven on a laptop with one server: the three DX Life branches rendered their maps,
7 of 7 checks passed, with internal/sync-dropin.mjs --from-config --dest <doxa-life>/public. This
makes that the default path:
- dropin.dest is ../doxa-life/public. The old ../doxa-life/public/embed/doxa-maps-build was stale.
- bun run dropin = sync-dropin --from-config; deploy:dev (build + drop-in) now uses it.
- The README gains "The easiest path": the studio for the work, dropin.dest at your site's public/,
then build and drop in.
One hazard came with the new destination, and it is closed here. --prune deletes every file in the
destination outside the drop-in's allowlist, which was right while that was a folder of its own. Into
a site's public/, it would delete the site. deploy:dev passed --prune, so it no longer does, and
sync-dropin refuses --prune into any folder named public/. Measured in a scratch site: --prune into
site/public exits 1 with the reason; a plain drop-in copies dx-maps/ and leaves the site's own
file in place.
The why now says what the maps are for, in the order they serve it: giving disciples, churches and
leaders what they need to bless the nations, with indigenous disciples, churches and leaders
multiplying and blessing their own peoples as the deeper answer to the Great Commission. It names the
three things the maps serve: praying for the unreached peoples of the world, mobilising believers to
bless their own cities, and caring for the needy with private maps.
Output 3 showed only the map app as a desktop app. Rudder is the desktop
example the README describes, and it has now been built with
`bun run desktop:build rudder-voice` and run on Linux: a screenshot of the
running app goes under the map app's, with one line on how to build it.
The PNG carries no text chunks (the original recorded its creation time
and timezone); the pixels are unchanged.
The worktree example branched from main and pushed to it; main is production now and the team lands
on development. The quick start cloned doxa-open/doxa-maps, which is not the repo, and sent readers to
localhost:5173 while the dev server takes the next free port when 5173 is busy (a fresh clone's friend
test came up on 5174).