Thermograph monorepo: graded-climate API + SSR frontend + infra, domain-specific containerized deploys
Find a file
Emi Griffith 177b123495
All checks were successful
PR build (required check) / changes (pull_request) Successful in 7s
secrets-guard / encrypted (pull_request) Successful in 6s
shell-lint / shellcheck (pull_request) Successful in 8s
PR build (required check) / build-frontend (pull_request) Has been skipped
PR build (required check) / validate-observability (pull_request) Has been skipped
PR build (required check) / build-backend (pull_request) Successful in 1m9s
PR build (required check) / gate (pull_request) Successful in 2s
data/climate: fix geocode_nominatim's NameError on every call
geocode_nominatim referenced _REVGEO_LOCK, which was removed when
reverse geocoding moved from a bare lock to the dedicated worker/queue
design -- the forward-geocode function was never updated to match, so
every call raised NameError, degrading to a 502 on the frontend. Live
on prod, beta, dev, main, and release since the lock was removed
(~30h): every comma-qualified name, postcode, and non-cities1000 place
502'd, while the local GeoNames index kept bare city names working, so
the failure was invisible to simple smoke checks. The three existing
tests covering this path all monkeypatch geocode_nominatim itself, so
none of them ever executed the broken body.

Fixed by routing forward-geocode jobs through the same worker/queue
that already serializes reverse-geocode jobs, instead of reintroducing
a second lock -- both job kinds are now drained by the one worker
thread, so _revgeo_last still has exactly one writer and the shared
~1/sec Nominatim pacing the docstring always claimed is now actually
enforced, not just asserted in a comment.

Adds regression coverage that exercises the real queue/worker plumbing
(not a monkeypatch of geocode_nominatim) plus a stubbed-HTTP test of
_fetch_geocode_forward's own body, the function whose earlier version
never once executed successfully.
2026-07-24 12:43:47 -07:00
.forgejo/workflows Log hygiene: Alloy CPU, Loki chunks/limits, Caddy field-stripping (#36) 2026-07-24 04:37:41 +00:00
backend data/climate: fix geocode_nominatim's NameError on every call 2026-07-24 12:43:47 -07:00
frontend shell: add shellcheck CI guard and drive the tree to zero findings (#19) 2026-07-23 22:26:05 +00:00
infra ops/iceberg.sh: read-only Iceberg lake queries across all environments (#23) 2026-07-24 19:34:30 +00:00
observability Log hygiene: Alloy CPU, Loki chunks/limits, Caddy field-stripping (#36) 2026-07-24 04:37:41 +00:00
CLAUDE.md docs: monorepo README, cutover runbook, root agent instructions 2026-07-22 22:11:33 -07:00
CUTOVER-NOTES.md docs: record the 2026-07-22 branch-migration sweep in cutover notes 2026-07-22 22:27:09 -07:00
README.md docs: monorepo README, cutover runbook, root agent instructions 2026-07-22 22:11:33 -07:00

thermograph

The Thermograph monorepo — the split repos reunified (2026-07-22) with full history via subtree merges, while keeping everything the split was actually for: per-domain images, per-domain deploys, and an async FE/BE contract.

Domains

Dir What CI
backend/ FastAPI graded-climate API, accounts, notifications (Discord bot, push, mail), data pipeline backend-build-push → image emi/thermograph/backend; backend-deploy[-prod|-dev]
frontend/ Public client: static JS/CSS + SSR pages frontend-* mirrors of the above; image emi/thermograph/frontend
infra/ Compose, deploy scripts, terraform, SOPS secrets vault, ops cron infra-sync (host checkout + secrets render), secrets-guard, ops-cron
observability/ Loki + Grafana + Alloy stack observability-validate

thermograph-docs deliberately stays its own repo (ADRs + runbooks, no build artifacts, different change cadence).

How CI stays decoupled

Every workflow in .forgejo/workflows/ is path-filtered to its domain: a push touching only frontend/** builds/deploys nothing else. Images stay separate (emi/thermograph/backend, emi/thermograph/frontend, each tagged sha-<12hex>), deploys stay per-service (infra/deploy/deploy.sh SERVICE=backend|frontend|all), and the API version contract (GET /api/version, PAYLOAD_VER) still lets FE and BE ship out of lockstep. The one intentionally coupled piece is pr-build.yml: a single always-running gate required check that builds only the domains a PR touches (a path-filtered required check would deadlock auto-merge).

Branch model (unchanged from the split era): PRs → dev, main → beta, release → prod; infra tracked via main on all hosts.

Before pointing anything live at this repo, read CUTOVER-NOTES.md.