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.
- On a phone the drawer started OPEN (navOpen = initialNavOpen) and mounted() closed it, so every load drew it
on screen and slid it away (measured: up to ~1.6 s, both shells, app and web mode). data() now reads the
same (max-width: 760px) query, so the first render is already the phone's closed state; mounted() still
tracks breakpoint crossings.
- In web mode the language at the drawer's foot sat right under the last row: margin-top:auto needs the nav's
flex column (.has-rail-end), which only the desktop rail-end set. The phone drawer's foot sets it too.
- check:shell-header asserts the flex column in web mode (14 planted defects).
ONE HEADER ON A WEBSITE. Inside a host site that has its own header, the shell's 44px phone bar stacked a
second white bar under it. VerticalTabsLayout gains shellMode (prop, or the host element's shell-mode
attribute): web | app | auto.
- app (a Capacitor or standalone app, or a DX Maps leaf page): the phone bar, unchanged.
- web: no bar. The rail opens from a 40px corner ☰ at the pane's bottom-start, which publishes its footprint
(--map-fab-clearance / --map-fab-beside) so the legend sheet, the pin hint and back-to-top step aside; the
language menu moves to the drawer's foot.
- auto: the host's shell-mode attribute, else app for Capacitor, display-mode standalone or a leaf
(window.MAP_OWNS_PAGE), else web.
Desktop is unchanged. check:shell-header renders both modes (13 planted defects); check:nav-reachable-mobile
accepts the corner ☰.
MAPS HOLD THEIR SIZE. ProfileLoader floored every map at min-height 100dvh, and the leaf pages' slot did the
same. dvh follows the mobile URL bar, so scrolling grew every map mid-scroll and moved its legend sheet. Both
now use 100svh (the viewport with the bar showing, which never changes on scroll), with 100vh where svh is
unsupported.
A host page can define a tag name before our bundle loads, and the first definition wins. DX Life's own
/js/doxa-simple-map.js defines <doxa-map> and <doxa-simple-map>, so on those pages our guarded define was
skipped and the page drew the host's element under our name.
- The doxa-simple-map bundle now defines <dxm-simple-map>, DX Maps' own name, first. The manifest, the leaf
pages and the gallery mount it (generate-tree reads the first define).
- <doxa-map> and <doxa-simple-map> are defined only when the host asks: data-legacy-tags on the bundle's
<script>, or window.DXM_LEGACY_TAGS = true before it loads.
countrySheet.js now withholds the whole-India poster (WITHHELD,
sheetWithheld), and makeCountrySheet returns nothing for India. The three
large-format Styles cards use India as their sample, so they showed
"preview unavailable" and their print fell through to a stored poster
that does not exist. They now answer PosterWall's unsupported with
messages.countryPosterTooMany, in the reader's language: the card shows
that sentence instead of a preview, and a print opens only that notice.
The designs stay in the catalogue, so they return when India is no longer
withheld.
India's whole-country poster does not ship yet; a solution is in progress. countrySheet.js gains WITHHELD (India)
and sheetWithheld(item):
- makeCountrySheet draws nothing for a withheld country, in any format, before fetching a single record.
- largeSheet() is null for it, so the Country Posters wall and the Regions list show their existing
messages.countryPosterTooMany ("Too many people groups to fit on one poster legibly — a solution is in
progress.", all eleven locales) on the card instead of a poster.
- Both callers also withhold its list, packet and cards with the same sentence (about a thousand pages, offered
when the poster is).
- India's STATE and union-territory posters are separate cards (indiaState) and are untouched; so are the India
section and its first place.
- sheetFormatFor keeps the large-sheet size rule itself (it returns with the poster).
check:country-sheet-one-path arm G: India stays withheld (no sheet in any of 7 formats, 0 records fetched, its
state cards never withheld, both callers show the sentence and reject its rows), with a poison for un-withholding
it and one for a wall that prints its list.
The facts row's COUNTRIES cell is ISO3_NAME[iso3] || iso3, and 13 territories that have country posters were
missing from the table, so their shipped sheets printed the raw code there (CCK, CXR, ESH, GIB, HKG, JEY, KOS,
MAC, MCO, NFK, SJM, SXM, TKL). Kosovo's own label in country-posters.json was the code too, so its title and
its wall card read "KOS".
- ISO3_NAME gains the 13 (the short names the posters already print as each label; SXM "Sint Maarten").
- KOS's label is "Kosovo".
Every country in country-posters.json now has a name, and no label is a raw code.
Measured on the shipped India sheets in the browser (getBBox, Lato loaded): 74 of 151 photos on the 60x96 and
32 on the 66x66 overlapped their own name, by up to 7 px (half a letter at 20.7 pt). The photo was placed from
an ESTIMATED text width, and the real width runs wider.
Now the photo sits in a fixed slot on the LEADER side of its name, and the name moves out by the slot. A
side-lane name is anchored at its leader-side end (left column: text-anchor end; right: start), so its glyphs
lie wholly on the far side of that anchor and the photo wholly on the near side: they cannot overlap whatever
width the font draws. Only a name with a photo takes the slot, so the columns and the type keep their size
(India 60x96 stays 20.7 pt).
Measured after: India 60x96 and 66x66 have 0 photos on, and 0 touching, their name. 0 proper crossings on all
three India formats. 36x60 (no photos) is byte-identical. Re-pinned.
- check:side-lanes: every photo on its name's anchor side, at the 4 px gap (a source twin, always measured).
A self-test poison restores the estimated-width placement: 167 photos off their side, caught.
- check:side-lane-photo-pixels (browser): every photo box clear of its name's real drawn box (getBBox), with
the same poison in its self-test.
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.
.github/workflows/pages.yml is now documentation/deploy/github-pages.workflow.yml, with a
one-line README beside it: copy it into .github/workflows/ in your own repository to deploy to
GitHub Pages. Nothing referenced the old path. The repo carries no .github/ folder any more, so
a host never runs it automatically.
On a phone the first-visit PinHint toast sits over the HUD's bottom edge, and the
message line under the buttons was covered there. A hit-test across the text
found the toast at 30%, and the screenshot read "This needs a" and no more.
The line (Copy's flash, the phone ceiling's notice, failures, the print's
progress) now sits under the hint at the top of the HUD, at 11px instead of 9px.
check:batch-selection-export pins it above the action rows.
.upg-status is display:none below 768px (the legend sheet owns the bottom edge),
so the phone ceiling's "This needs a desktop or laptop computer." and the print's
progress ("fetching people groups…", "printing 905 people groups…") were written
only where a phone shows nothing. A reader tapped Prayer List and nothing visible
happened.
hudSay writes a batch action's message to the batch HUD's line (.upg-hud-flash,
now a polite live region, visible at every width) as well as to the status
badge. The block notice stays 6 s and failures 5 s. The print's progress is
mirrored into the same line whenever no flash is up, and is cleared when the
print ends.
check:batch-selection-export arm C checks that the notice, every batch action's
failure text and the progress reach the HUD line, that no batch action writes
via flashStatus alone, and that no rule hides .upg-hud-flash. Four mutants each
fail, including "notice written only to .upg-status".
Building a selection's Prayer list or Card deck is never the problem (the whole
selection is ready in under 30 s on a 4×-throttled phone). PRINTING it is: in a
browser capped at 2.5 GB, the print of 1,857 groups (101 sheets, a 138 MB PDF)
finished, and 2,209 groups (113 sheets) crashed the page at 1× and at 4×. A phone
tab has roughly half that memory, so above 1,000 groups (PHONE_GROUP_CEILING,
with the measurement beside it) a phone or tablet gets the house text
"This needs a desktop or laptop computer." (poster.phoneLimitAll, as Country
Posters shows for whole India) and nothing starts. India alone (905) still
prints on a phone. A desktop is never blocked. Copy and CSV stay allowed.
The device test is isPhonePrint(), the same platform test the print path uses.
check:batch-selection-export arm A7 tests the ceiling both ways. Arm C checks that
Prayer list, Card deck and Poster each ask BEFORE they print, and arm D checks
the string ×11. Four mutants each fail.