Commit graph

38 commits

Author SHA1 Message Date
doxa-maps
e2c5c2ec49 Gate: every shipped profile must say what it is
Some checks are pending
build-gate / build (push) Waiting to run
Last tick fixed 15 of 26 profiles that shipped with an empty description.
Without a gate that fix has a half-life: the north star is "develop a plugin
and see it live", so new plugins arrive often and each one is a fresh chance
to regress this silently. The repo already validates @element, but only
against a MALFORMED value — an ABSENT @description fails quietly, the
generator writes an empty string, every gate passes, and the defect only
surfaces to a human who opens the studio and reads.

Reads the BUILT manifest, not the .vue sources, on purpose. Re-deriving the
description here would mean reimplementing generate-tree's TAG_RE — which is
exactly what hid two of the fifteen last tick, when a hand-written copy of
that regex reported success while the real rule extracted nothing. Check the
artifact the consumer reads, never a reimplementation of how it was produced.

MIN_CHARS is calibrated against the real corpus, not guessed: shortest
genuine description 62 chars, median 81, longest 192, so the 25-char floor
sits clear of every legitimate entry while still catching a stub.

Negative-tested four ways, including end to end: deleting a @description tag
from a .vue source FAILS THE WHOLE BUILD (exit 1) naming the profile and the
file to edit; restoring it returns the build to green. Also fails on a short
stub and on a near-empty manifest (the vacuous-pass guard), rather than
passing for lack of anything to check.
2026-09-22 03:23:55 +00:00
doxa-maps-88655c70
9dc4dda8cf consolidate docs into documentation/ and deployment shells into deployment/
Some checks are pending
build-gate / build (push) Waiting to run
Driver 2026-09-21: 'a folder called deployment which will store the capacitor
folder, wordpress folder and tauri folder', and 'a folder called documentation
to store the contributing folder so docs are in one place'.

docs/ + contributing/ + CONTRIBUTING.md -> documentation/ (three doc homes into
one). capacitor/ + wordpress/ + src-tauri/ -> deployment/.

The public/ -> dist/ rename the Driver asked about is NOT here: after seeing the
measured blast radius (855 tracked files, 54 source refs, and public/ is the
served web-root four consumers point at) the Driver withdrew it - 'keep the
public folder the same'.

Path references were rewritten mechanically (258 across 87 files), but three
classes needed hand fixes because a prefix regex cannot see them:
  - join(ROOT,'docs',...) in 10 internal scripts - a quoted SEGMENT, not a prefix
  - tauri frontendDist '../public' -> '../../public', since src-tauri is now one
    level deeper; check:tauri caught this exactly as the card predicted
  - the WordPress contract test's dirname(__DIR__,2) -> 3, same reason

Also reverted one over-reach: the rewrite renamed a FINDING-CATEGORY label
'docs' in public-hygiene-check.mjs, which is a rule name in its own header, not
a path.

Verified: full build ALL GUARDS PASS, SRI consistent; check:tauri, check:mobile
(cap sync + webDir OK), check:wp-embed, quoted-scripts, capability-citations,
capability-exemplars, library-index, public-hygiene, parse and drop all pass.
2026-09-21 23:23:23 +00:00
doxa-maps-88655c70
0c7bfb9b9c add a catalog CURRENCY gate: is the live tree the build we have?
Some checks are pending
build-gate / build (push) Waiting to run
check:catalog-identity hashes each live bundle against the CATALOG'S OWN
manifest - deliberately, per its header. That proves the served tree is
internally consistent; it can never prove it is current. A catalog three
publishes behind is perfectly self-consistent and passes clean.

Measured today: after baking the Mapbox token, catalog-identity reported '17 of
17 identity-proven - PASS' while every live leaf still served BAKED_TOKEN = ''
and every map on the platform was broken.

The two gates answer different questions and neither subsumes the other:
  identity  is the live catalog consistent WITH ITSELF?  (tamper/corruption)
  currency  is the live catalog the SAME BUILD as ours?  (staleness)

It checks BOTH bundles and leaf pages, because a bundle-only version of this
gate also passed: baking the token changed 37 leaf HTML pages and ZERO .js
bundles. generate-tree owns the leaves, vite owns the bundles, and a publish can
change either independently.

Current reading: bundles 17 current, leaves 17 STALE.

Negative-tested: unreachable catalog SKIPs (offline must not fail a build), a
manifest yielding 0 integrity values FAILs rather than passing vacuously, and a
missing local build SKIPs.
2026-09-21 23:05:53 +00:00
doxa-maps-88655c70
ea53a2e19a emit a real desktop binary and .deb for deploy method 5
tauri build produced target/release/doxa-maps-desktop (65 MB ELF) and a 58 MB
.deb (Package: doxa-maps, Depends: libwebkit2gtk-4.1-0 libgtk-3-0). tauri info
reports the environment fully green and correctly detects frontendDist
../public, Vue.js, Vite.

The web tree is compiled INTO the binary rather than shipped as files, which is
why the .deb holds only 6 entries - confirmed by 19 distinct embedded
doxa-maps/... asset paths in the ELF and by size (a logic-free shell is 65 MB
only because the 132 MB public/ tree is compressed inside it).

bundle.targets narrowed from all to deb: rpmbuild and linuxdeploy are absent
here, so all would fail two of three formats at the END of a ~6-minute release
build.

The CLI is a devDependency (@tauri-apps/cli, prebuilt binary) rather than
cargo install tauri-cli, which compiles from source.

Status stays BUILT, NOT SHOT. The GUI launch is unverified because this box is
headless - the binary panics in tao with 'Failed to initialize GTK', the
absence of a display rather than a defect, and no Xvfb is installed.
2026-09-21 21:36:18 +00:00
doxa-maps-88655c70
cad913bc6f scaffold deploy-method 5: Tauri desktop shell for the cap-5 SPA
Method 5 was the last aspirational entry in the 5-deploy-method model: 0
tauri.conf.json, 0 Cargo.toml, 0 .rs. It is now scaffolded against the v2
config schema, mirroring the Capacitor pattern that already works.

frontendDist points at the SAME public/ tree Capacitor and the CDN serve, so
one build still feeds every consumer - now four instead of three. The crate
carries no application logic on purpose: the product is the web tree, this is
a window around it.

check:tauri is the analogue of cap:verify and exists for the same reason
capacitor.config.json warns about in capitals: a wrong frontendDist or a window
url that does not exist launches a BLANK WHITE WINDOW, not an error. It runs
with no Rust toolchain and no Tauri CLI, so CI can run it. Negative-tested five
ways - wrong frontendDist, missing window url, identifier drift from the
Capacitor appId, empty windows array, missing crate file - each exits 1, and
the config was restored byte-identical after every test.

Status stays BUILT, NOT SHOT. rustc parses main.rs (it reaches line 14 and
fails only on the uninstalled tauri crate, a dependency error not a syntax
one), but producing a binary needs the Tauri CLI plus system webview libs.
Also recorded the token/CORS caveat up front: a Tauri webview serves from
tauri://localhost, so the token endpoint must send CORS for that origin -
the same surprise Capacitor documents for https://localhost.
2026-09-21 21:06:04 +00:00
doxa-maps-88655c70
c401a758bb name the legend collapse control and gate every button's accessible name
Last tick's fix covered the shared map-control base. Sweeping the rendered DOM of all 17
bundles found three more unnamed buttons — the same "Collapse legend" control in three
map bundles, again from one shared component. Its only content is an svg caret, so with
title alone its accessible name was empty.

It is a disclosure control, so it now reports aria-expanded as well. Verified by toggling
it: the label flips Collapse/Expand and aria-expanded flips true/false.

The gate measures the rendered DOM through shadow roots, because a source grep cannot
answer this question. There are 132 <button> hits here and most have visible text, which
IS a valid accessible name; aria-label is often on a following line. Only the DOM knows
which buttons ended up nameless. It counts aria-label, a resolved aria-labelledby, or
visible text, and deliberately does not count title, which is the defect it was written
for. Hidden buttons are skipped since assistive tech cannot reach them either.

The sweep went 319/322 before the legend fix to 322/322 after, which is the evidence it
detects real defects rather than passing vacuously.
2026-09-21 13:23:35 +00:00
doxa-maps-88655c70
d0568ebd35 prove the live server serves the catalog's exact bytes
Card 1 asks that one public Forgejo repo be the live catalog source and that the merge
lose nothing. Both halves were marked done-verified on 2026-09-19, two publishes ago, so
this re-measures rather than trusting the field: the catalog is still a zero-diff match
for the freshly-built tree, all 17 live bundles hash to their own manifest integrity, and
the live public manifest is byte-identical to the catalog's.

An HTTP status cannot make that claim. /app/doxa-maps/manifest.json returns 200 and is a
2,872-byte HTML sign-in page, so a status check reads the login wall as healthy. Every
profile already carries an integrity hash the build computed for an unrelated reason, so
comparing it against the live bytes is a cryptographic identity proof with no new source
of truth to keep in sync.

Network-dependent, so not in build:unlocked and not in CI. It distinguishes "could not
reach" from "reached and wrong" and only the latter fails, because a gate that fails on a
network blip gets switched off.

Negative-tested four ways: the login wall fails with the reason, an unreachable host
skips at exit 0, the catalog-comparison mode confirms the sha256 match, and the baseline
proves 17 of 17.
2026-09-21 12:46:48 +00:00
doxa-maps-88655c70
466493ed80 generate the capability exemplar table so the answer stops being remembered
"Which bundle is the capability-N example?" has been answered wrongly in three separate
artifacts, each fixed in isolation while the others kept the bug: verify-5-capabilities
cited doxa-vision-card as cap-1, the README made the identical claim, and the
/plugin-designer skill still cites both that and unified-research-dashboard, a bundle
that does not exist.

The skill cannot be gated from this repo. It lives in the session config directory, in
no git repo, so a checker pointed at it would pass here and skip silently everywhere
else — the gate-that-cannot-fail shape. So instead of policing the skill, give the repo
an authoritative generated answer that prose can cite and a gate can verify.

The table derives from manifest.json, which the build computed from each folder's real
shape, and reference.md now points at it with the reason: a folder's apparent simplicity
does not decide its tier. doxa-vision-card looks like the lightest thing in the repo and
is capability 2, because it ships an instances file.

check:capability-exemplars regenerates and content-compares rather than checking mtimes,
because a full build rewrites manifest.json every time and a gate that cries wolf after
every build gets switched off.
2026-09-21 12:38:50 +00:00
doxa-maps-88655c70
c7a228e941 shoot a real cap-1 element and gate README capability claims against the manifest
The CAP 1 slot shot doxa-vision-card, which is not a single element: it ships
doxa-vision-card.instances.json with two instances, so discover.mjs types it
parameterized and manifest.json records "parameterized" — capability 2.

The same error had already been found and fixed in verify-5-capabilities.mjs, whose own
comment records that it had been sampling cap-2 twice and cap-1 never. That fix landed in
the gate and never reached the README: two artifacts encoding one fact, with nothing
forcing agreement. Fixing one copy was not fixing the fact.

all-people-groups is a genuine cap-1 — one .vue, no instances file, manifest type single.
The README now also says what decides the tier, since the folder's apparent simplicity is
the trap: doxa-vision-card looks like the lightest thing in the repo.

check:capability-citations compares every README capability claim against the manifest,
blocking in CI. Its first matcher used \Z, which is not JavaScript, so it matched zero
sections while still reporting a citation count — a gate reporting a number it had not
verified. It now splits on headings and the count reflects real matches.
2026-09-21 12:02:18 +00:00
doxa-maps-88655c70
15ce17f907 fix the impossible 27/26 zone badge and replace a faked page-2 shot with the real third profile
The badge read "27/26 zones" — a count exceeding its own total — in two shipped README
screenshots. 26 was the leaf count before slice 09b-6 added the Frontier-Asia uncovered
rows; the literal was never updated, so it drifted the moment the data grew. The
denominator is now derived from perZone.size, which tracks the zone list automatically
and cannot disagree again. Measured from clusters.json: 13 regional + 12 asia + 2
uncovered + 1 deaf = 28. The corrected badge also surfaces 1 genuinely unresolved zone
that the stale denominator was masking.

legend-tree.js asserted "33 leaves" and "7 Frontier-Asia uncovered" in a comment; the
shipped data carries 28 and 2. Corrected to point at the arrays instead of a count.

README showed "List — page 1" and "List — page 2" as the same picture. The shot loaded
one URL twice and tried to reach a page 2 by scrolling and clicking Next; neither exists,
the list has no pagination, and every attempt sat in try/catch so the miss was silent.
The PNGs differed by 0.16% render noise, so no checksum would have caught them. Mean-
while poster-wall — a real manifest-listed profile — had no screenshot at all. Shoot the
real third thing rather than fake a fourth.

check:shot-distinctness gates this, blocking in CI. It keys on url+viewport, so the
deploy2 mobile shot of the same SPA stays legitimate rather than a false positive.
2026-09-21 11:29:25 +00:00
doxa-maps-88655c70
5567134978 shoot the plugin-studio element and stop captioning the product SPA as the studio
README drew the two cap-5 products apart in prose and even warned that captioning a
studio shot as the product SPA is a documented mistake — then filled its plugin-studio
slot with cap5-doxa-maps-spa.png. Both cap-5 figures showed the same app and no
plugin-studio screenshot existed anywhere in the repo.

A slot filled with the wrong real image looks exactly like a correct one; nothing 404s.
So the fix is to shoot the missing artifact, not re-caption around the gap. Both cap-5
slots now use generated, freshness-gated images.

Also adds check:shot-fidelity. The shot run reported 13x OK while every emoji rendered
as a tofu box: this machine has no emoji font. Measured rather than assumed — the DOM
carries 27 correct codepoints, so the markup is right and the defect belongs to the shot
machine. Reports by default because installing a font is a Driver call; --strict blocks
once one is present.
2026-09-21 10:59:28 +00:00
doxa-maps-88655c70
a853081e3f gate the docs' quoted scripts block against package.json
README-SECTION-3 reproduces the scripts block verbatim and carries its own plea to
re-transcribe after every package.json edit. It drifted anyway, twice: cap:sync still
showed the bunx form that does not exist here, and build:unlocked was missing
check:public-hygiene and check:label-joins.

The second is the dangerous shape. An omission in a quoted list has no wrong character
to notice, so a reader auditing what the build verifies concludes the secret scan is not
in the chain. The warning was already there and did not work, because re-transcribing by
hand is what gets skipped when the edit felt unrelated.

Blocking in CI: it diffs two files already in the checkout, so a failure is always real.
Negative-tested both ways, including a matcher that matches too little failing rather
than passing vacuously.
2026-09-21 10:47:51 +00:00
doxa-maps-88655c70
e3136a4ec1 generate the library menu and gate it against library drift 2026-09-21 07:25:18 +00:00
doxa-maps-88655c70
32e6c1231e gate readme screenshots against the bundle they show; wire the generator 2026-09-21 07:05:00 +00:00
doxa-maps-88655c70
7276ade0e6 wait for svg not canvas on the chart dashboard; refresh all readme shots 2026-09-21 06:58:48 +00:00
doxa-maps-88655c70
22d4c9bedb require evidence on a done card; baseline the ten pre-existing 2026-09-21 06:34:30 +00:00
doxa-maps-88655c70
41d089de10 add a composite-freshness gate for stale nested children 2026-09-21 06:06:29 +00:00
doxa-maps-88655c70
ab04635e58 add a VCS-independent witness for the read-only DX-life clone 2026-09-20 09:45:25 +00:00
doxa-maps-88655c70
4970059e55 add a label-join gate: fail on any new equality predicate comparing two independently-derived values 2026-09-20 09:05:18 +00:00
doxa-maps-88655c70
0149b686a2 wire the public-hygiene gate into the build; the 41 findings it was blocked on are gone 2026-09-20 08:39:41 +00:00
doxa-maps-88655c70
cdfb895a9d add check:mobile, a deploy-2 contract gate that needs no toolchain 2026-09-20 08:19:13 +00:00
doxa-maps-88655c70
fc230bd218 invoke the local capacitor CLI directly; bunx is not on PATH and broke the mobile deploy chain 2026-09-20 08:18:25 +00:00
doxa-maps-88655c70
a4c6524b3a add a WordPress embed-contract test that runs without WordPress, and harden the SRI filter against a non-string handle 2026-09-20 07:25:54 +00:00
doxa-maps-88655c70
79d46126de Add 5-capability verification gate: proves every capability level mounts, renders and is error-free 2026-09-20 04:44:07 +00:00
doxa-maps-88655c70
4c27190641 Add SRI-consistency and settled-tree gates; wire SRI check into the build chain 2026-09-20 03:23:40 +00:00
doxa-maps-88655c70
318147ea52 Add map-paint verification gate; fix template-literal guard false positive on JSDoc; wire guard first in build chain 2026-09-20 02:49:21 +00:00
doxa-maps-88655c70
ea0c47b277 Brand-neutral shared VerticalTabsLayout with lazy nested-element hosting; fix cap-5 prayer tab leaf path; neutralize library identifiers; add Forgejo CI build-gate workflow
Some checks failed
build-gate / build (push) Has been cancelled
2026-09-19 17:24:30 +00:00
doxa-maps-88655c70
1a3925c6a6 Rename app-profiles -> plugin-designs; build green under new naming (load-bearing paths updated, guards pass) 2026-09-17 18:18:18 +00:00
swift-river542
9cefd6faa4 Plugin gallery: one generator for the dev server and the build
- internal/playground → internal/plugin-gallery; playground-smoke → plugin-gallery-smoke;
  generate-spa → generate-gallery (scripts: gallery, smoke:gallery; smoke:playground stays as alias)
- generate-gallery exports renderGallery(model, { mode }) — the dev plugin serves the same page
  at / from the discovered registry (dev mode imports the dev entries, ladder popovers on);
  the build writes public/index.html from the manifest (byte-identical to before)
- Docs and probes use the gallery vocabulary
2026-09-16 20:04:18 -05:00
swift-river542
60ad81bf5f Drop-in destination declared once in the build config (dropin.dest); sync-dropin and deploy:dev read it, DOXA_DROPIN_DEST overrides
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
2026-09-16 19:20:23 -05:00
swift-river542
3baa0afaf8 Website drop-in destination is public/embed/doxa-maps-build
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
2026-09-16 18:42:15 -05:00
swift-river542
12d1fee133 Public build folder is three entries: index.html, assets/, doxa-maps/
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
- index.html is generated by internal/generate-spa.mjs from doxa-maps/manifest.json: one
  self-contained page that lists every bundle, profile and instance, mounts the chosen one,
  and offers Open page / Copy embed; build ends with `bun run spa` (the material shell is no
  longer installed; nav.js, icons.js, languages.js, pane-wrapper/, external/ and _headers
  leave public/)
- The website drop-in follows the same contract: dropin.include in the build config,
  sync-dropin --from-config --prune, public-hygiene --strict fails on any other root entry
- Production bundles no longer carry CSS comments: a build-only Vite plugin strips them from
  every shadow-style block (about 140 per map bundle, 5% smaller); remaining comments reworded
- Share pages and the pin renderer drop their note-style comments
2026-09-16 18:23:14 -05:00
swift-river542
7f6c2286f9 Legend table columns no longer overlap: numeric columns scale with the type
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
- SemanticTreeLegend: the UUPGs/Population columns were a fixed 44px, sized for the old
  9px header / 10.5px numbers; at the desktop scale "POPULATION" collided with "UUPGs"
  and "139.0M" spilled into the neighbouring column. Base column is now 60px, desktop
  76px (header 10px, numbers 13.5px, no wrapping)
- The panel follows --map-legend-width like the detail card and the geocoder pill; the
  research map's wide-host legend is 400px so "Full Prayer Coverage" stays on one line
- New probe: `bun run probe:legend-columns` measures cell boxes vs real text extents and
  fails on any spill or overlap (desktop and phone sheet)
2026-09-16 17:34:21 -05:00
swift-river542
0f9027f592 Public hygiene: sync:dropin excludes staging pages by default; check:public-hygiene gate scans a drop-in for leaks, secrets, source maps, docs and preview surfaces
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
2026-09-16 13:04:36 -05:00
swift-river542
a876e5e97b Host-agnostic share links, token-endpoint chain, dev playground as reusable modules, share/asset gates, validated profile-type vocabulary
- ShareButton resolves its link from where the bundle is hosted (profile-config.shareUrl ->
  embedBaseUrl -> window.MAP_EMBED_BASE_URL -> the bundle's own script URL); profiles pass
  tree-relative embed pages, so links work in the playground, on doxa.life and on any CDN
- Generated leaf pages try token endpoints in order (override -> configured absolute URL ->
  same-origin) and only then offer the paste-once box; build config points at the canonical
  https://doxa.life/api/maps/token
- Dev playground extracted from vite.config.js into internal/playground/{discover,render,dev-plugin}.mjs;
  serves /assets/* like the built site so data-driven maps initialise; passes assetsBase +
  shareUrl to every element; header ladder with a Start-here primer and the five kinds of
  mounting files (single-element, preset-element, multi-view-element, composite-element, app-shell)
- New gates: smoke:playground (every element boots, with status text + data diagnostics),
  smoke:share (Share URL resolves and reopens the same map), sync:dropin (mirror public/ into a
  host checkout, never credentials), deploy:dev (build + sync)
- MapToolbar + ShareButton on every map profile; UPG-100 composite (map + prayer lists) on the
  pane layout; shared --map-radius token consumed by the legend components
- docs/reference.md: profile types table, profile-config assetsBase/shareUrl/embedBaseUrl, gates
2026-09-16 12:10:38 -05:00
doxa-maps
680a714bdc embed-smoke: prove every map embeds (17/17 pages mount cleanly)
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
New permanent harness (bun run smoke:embeds): loads every profile page from the
partner-facing manifest headless (SwiftShader software WebGL — no GPU needed),
asserts the custom element defines + populates its shadow DOM, no ProfileLoader
error card, and a WebGL canvas on map profiles. First full run: 17/17 pass —
8 map profiles paint a canvas, 9 DOM profiles (lists/tables/charts/walls)
populate. Exit 1 on any failure, so CI can gate on it.
2026-09-16 02:25:33 +00:00
doxa-maps
c7260faaa3 feat: map provider swap — mapbox|maplibre engine + hybrid geocoder + provider badge; staging title doxa-maps 2026-09-11 21:47:25 +00:00
doxa-maps
d5ba21fa76 doxa-maps — portable bundler for embeddable JAMstack maps
Standalone repo that builds embeddable JAMstack map bundles (web components),
composes them into a lazy-loaded SPA via layouts/, and optionally wraps a
Capacitor Android/iOS app — one build, choose your output/CDN. Diátaxis docs.

History reset to a single clean commit; no PII, IPs, or internal terms.
2026-09-10 01:53:07 +00:00