# (The build no longer uses an intermediate app/ tree — vite writes straight into # public//, which is committed. See "THE SERVED WEB-ROOT" below.) # Node node_modules/ .npm/ .pnpm-store/ # Vite cache .vite/ dist/ # Editor .vscode/ .idea/ # OS .DS_Store # Env .env .env.local .env.*.local !.env.example # Research dashboard — developed on its own branch (research-dashboard), # excluded from the map-only bundle. Do not track dashboard files here. # Local build artifacts / deploy config — kept on disk, out of the map PR build-sizes-baseline.json _archive # ── BUILD OUTPUT — regenerated by `bun run build`; one tree, three consumers ── # Not committed: it is derived. Railway builds it in CI; Capacitor copies it via # `cap sync`; staging serves it locally. # ── Capacitor generated / machine-specific (the native projects THEMSELVES are # committed under capacitor/; only their build output is ignored) ── capacitor/android/app/src/main/assets/public/ capacitor/android/app/src/main/assets/capacitor.config.json capacitor/android/app/src/main/assets/capacitor.plugins.json capacitor/android/build/ capacitor/android/app/build/ capacitor/android/.gradle/ capacitor/android/local.properties capacitor/android/keystore.properties capacitor/ios/App/build/ capacitor/ios/App/Pods/ *.jks *.keystore # ── THE SERVED WEB-ROOT — COMMITTED ─────────────────────────────────────────── # public/ is the product: the shell, the built bundles and the pre-generated # posters the app fetches at runtime. It is committed whole so a clone (or a # static host, or the source-control service) has a working site without running # anything, and so CI regenerates only public// in place rather than # assembling the folder from other directories. # The exceptions are BOTH Mapbox credentials: token.local.js (local staging, held # out by public/.gitignore) and api/maps/token.json, which the build writes from # the MAPBOX_TOKEN environment variable so the value lives in deployment settings # rather than in git history. public/api/maps/token.json # ★ AND the EXTENSIONLESS path — the client requests /api/maps/token (never .json), so # write-token.mjs emits BOTH. This line was missing when the bare file was added, which # would have made a live pk. token committable. Same secret, second path. public/api/maps/token # ── stale dev-time bundle copies inside the wrapper ─────────────────────────── # The wrapper mounts bundles from ./doxa-maps/ at the served root now; these were # hand-copied during bring-up and would silently go stale. layouts/*/profiles/ layouts/*/js/ # ── POSTER GENERATOR OUTPUT ─────────────────────────────────────────────────── # library/scripts/posters/ is source (the template + its snapshots); out/ is what # it renders — ~95 MB of HTML and 3000x3000 PNGs, all reproducible from source: # bun run posters && bun run posters:shoot && bun run posters:gallery # ── EXCEPT the three inputs the build guards read ──────────────────────────── # iso2-map, resource-join and tables-complete verify shipped data against these. # They are derived, but they are derived from NETWORK FETCHES, and the research # network policy forbids CI reaching out to those sources — so CI cannot rebuild # them and, without them committed, three guards fail on a clean clone and the # deploy dies. 2 MB of the 23 MB out/ tree; the rest stays ignored. # Visual-gate screenshots are OUTPUT, not fixtures. visual-gate.mjs and # palette-preview.mjs only ever page.screenshot() INTO this folder — the gate's own # rule is that it never reads a PNG back, it asserts against the live DOM. So these # are 11 MB of regenerable pictures of somebody's screen, and they bake build-slice # vocabulary into pixels where no text scrub can reach it. plugin-designs/*/data/visual-gate-shots/ library/scripts/*/out/** internal/*/out/** # EXCEPT the three inputs the build guards read. iso2-map, resource-join and # tables-complete verify shipped data against these. They are derived — but derived # from NETWORK FETCHES, and the research network policy forbids CI reaching those # sources, so CI cannot rebuild them. Without them committed, three guards fail on a # clean clone and the deploy dies. 2 MB of the 23 MB out/ tree; the rest stays out. # (Negations must follow the exclude: the last matching rule wins.) !library/scripts/research/out/arclight-languages.json !library/scripts/research/out/iso3-to-iso2.json !library/scripts/research/out/language-resources.json !library/scripts/research/out/ebible-translations.csv !library/scripts/research/out/tables/ !library/scripts/research/out/tables/** # ── INTERNAL DOCS — the split-plane rule ────────────────────────────────────── # Agent/internal working docs live here and are NEVER published. The repo's # public surface stays architecture + code + contributor docs; internal process # notes, lessons-learned and agent instructions stay out of it. internal-tracker/ # ── GENERATOR OUTPUT — regenerable render artifacts, never committed ────────── # internal//out/ holds what the poster/research generators RENDER # (upg100-posters/out alone is ~619MB / 12,623 PNGs). The generators themselves # are source and ARE committed; their output is derived. # # ⚠ THIS BLOCK USED TO SAY "what actually ships is the exported subset in # public/assets/ (see export-public.mjs)". BOTH HALVES OF THAT WERE STALE, and # the sentence is why 80.7 MB sat committed twice: # - export-public.mjs does NOT write public/assets/. It writes a SIBLING OF THE # REPO (../../../../upg-100-posters, or $DOXA_PUBLIC), and says so in its own # header — explicitly "NOT inside public/, because capacitor packages public/ # whole and these are 52 MB that must not ship in the APK". # - public/assets/ is now a GENERATED MIRROR of the tracked data/, # rewritten by generate-tree.mjs on every `bun run tree`. It is derived, not # exported, so it is ignored in public/.gitignore — which carries the # measurements and the one exception (previews/, which has no data/ source # and must stay tracked). # The tracked source for shipped assets is data/. # Private datasets never live in this repo (see DOXA_PRIVATE_DIR in # library/scripts/research/build-tabulator-tables.mjs). Guard against re-creation. data-private/ # ── public/ is the OUTPUT FOLDER + README.md only (the agent decision, BUILD-OUTPUT-IS-ONE-FOLDER, 2026-09-25) ── # These rules used to live in public/.gitignore, which was itself a tracked file INSIDE the generated # folder (so `rm -rf public && bun run build` deleted it). Local-only files stay untracked: /public/token.local.js # the build mutex's stamp (internal/build-lock.sh) — runtime state, now at the repo root /.build.lock # what the OLD layout wrote beside the tree; internal/clean.mjs removes any a checkout still has /public/assets/ /public/__gallery/ /public/index.html # Local plugin backends (backend//) — the compiled binaries are rebuilt from source, never committed. /backend/bin/ # The local PocketBase's data (`bun run dev`, internal/backend-dev-plugin.mjs): per laptop, never committed. /.pb/