Thermograph monorepo: graded-climate API + SSR frontend + infra, domain-specific containerized deploys
Find a file
Emi Griffith b1e0218147
All checks were successful
shell-lint / shellcheck (pull_request) Successful in 11s
PR build (required check) / validate-observability (pull_request) Has been skipped
PR build (required check) / gate (pull_request) Successful in 2s
secrets-guard / encrypted (pull_request) Successful in 7s
PR build (required check) / changes (pull_request) Successful in 12s
PR build (required check) / build-frontend (pull_request) Has been skipped
PR build (required check) / build-backend (pull_request) Successful in 50s
climate: stop dropping the current day from the Open-Meteo bundle
The recent+forecast bundle excluded the cell's in-progress local day, on the
premise that Open-Meteo's daily high/low for today aggregates only the hours
elapsed so far. That is true of the MET Norway series -- which starts mid-day
and is correctly gated on diurnal coverage in _metno_to_frame -- but not of the
forecast endpoint, which backfills the rest of today from the model run. The
guard was generalised across by analogy and never verified against the API.

Checked against the live API at four cells between 01:00 and 19:00 local: the
reported daily max always equals the max over all 24 hourly values, and differs
from the elapsed-hours-only max whenever the day has hours left. Los Angeles at
09:16 local reported 93.9F -- the whole-day figure -- where the hours so far
topped out at 76.1F.

The cost was a hole in the middle of every freshly-synced cell's window: a
complete yesterday, no today, a forecast tomorrow, which the daily strip renders
as a missing column. 861 of 1004 prod cells had lost the 25th by the time this
was diagnosed; the 143 that still had it were stale caches predating the change.
FORECAST_DAYS is commented "today + 7 days ahead", which the drop contradicted.

Today is kept unconditionally now. A day that truly cannot be graded, because it
came back with no high/low, is already dropped upstream by _to_frame, so the
local-today helper has no remaining caller and goes with it.

Also removes a verbatim duplicate of both Open-Meteo day-window tests, which the
main-into-dev reconciliation left in the file (the second copy shadowed the
first, so only one of each was ever running).
2026-07-25 09:22:46 -07:00
.claude guardrails: enforce live-host and secrets policy with hooks (#82) 2026-07-25 07:09:11 +00:00
.forgejo/workflows ci: collapse the eight deploy and build-push workflows into two (#87) 2026-07-25 07:48:49 +00:00
backend climate: stop dropping the current day from the Open-Meteo bundle 2026-07-25 09:22:46 -07:00
frontend docs: rewrite the agent context layer to match the live system (#81) 2026-07-25 07:08:54 +00:00
infra ci: collapse the eight deploy and build-push workflows into two (#87) 2026-07-25 07:48:49 +00:00
observability docs: rewrite the agent context layer to match the live system (#81) 2026-07-25 07:08:54 +00:00
.gitignore Drop accidentally-committed worktrees; ignore .claude/worktrees 2026-07-24 15:57:34 -07:00
CLAUDE.md ci: collapse the eight deploy and build-push workflows into two (#87) 2026-07-25 07:48:49 +00:00
CUTOVER-NOTES.md ci: collapse the eight deploy and build-push workflows into two (#87) 2026-07-25 07:48:49 +00:00
README.md ci: collapse the eight deploy and build-push workflows into two (#87) 2026-07-25 07:48:49 +00: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 build-push → image emi/thermograph/backend; deploy
frontend/ Public client: static JS/CSS + SSR pages same build-push / deploy workflows, matrixed by domain; image emi/thermograph/frontend
infra/ Compose (beta, LAN dev) + the Swarm stack (prod), 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.