thermograph/.claude/ownership.md
Emi Griffith 890cb4a764
All checks were successful
PR build (required check) / changes (pull_request) Successful in 6s
secrets-guard / encrypted (pull_request) Successful in 5s
PR build (required check) / build-backend (pull_request) Has been skipped
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) / gate (pull_request) Successful in 1s
docs: the orchestrator runbook and the module map
.claude/BRANCHING.md — how the trunk-based flow is actually run: the serialised
merge queue (Forgejo has no native one, so whoever orchestrates is it), the two
promotions and who decides each, and the hotfix down-merge that has to happen in
the same session or it becomes a regression scheduled for the next promotion.

Two things it states because getting them wrong is expensive here. CI does not
gate PRs into `release` — pr-build.yml fires only for dev and main — so a
required check on that branch would make every production promotion unmergeable
with a 405 naming no cause; closing that gap means changing the workflow first
and turning the requirement on second. And the chain cannot fast-forward: every
promotion leaves a merge commit on the target the source never receives, so
promotions are merge commits until someone deliberately reconciles the branches.

.claude/ownership.md — which tasks may run concurrently, seamed along the four
domains, plus the shared spine that has to be serialised: the workflows (CI
lives only in the root .forgejo/), env-topology.sh, the deploy entry points and
the CLAUDE.md files. Names the recurring collisions too — a schema change spans
alembic and whatever reads the column, and if the payload shape moves it is
PAYLOAD_VER and the frontend as well, which is one task and not two.
2026-07-26 12:53:33 -07:00

3.7 KiB

Module map — what can be worked on concurrently

Used for partitioning work across concurrent subagents. The rule is one owner per file at a time: never give two concurrent agents tasks that touch the same module. If two tasks genuinely need the same file, sequence them instead of racing them — a rebase conflict costs more than the parallelism saved.

Read this before dispatching. It answers one question: can these two tasks run at the same time?

The monorepo's four domains are the natural seam, and they are a real one: FE and BE build and deploy independently, and deploy.yml checks per-leg whether a push actually touched that domain. Two agents in two different domains will not collide in CI either.

Safe to assign concurrently

Module Files Notes
Backend — API backend/api/**, backend/app.py Route handlers and serialisation.
Backend — core backend/core/** Grading, percentiles, climatology. The domain logic.
Backend — accounts backend/accounts/** Auth, sessions, OAuth links.
Backend — daemon backend/daemon/** Scheduled work. Anything named "prefetch" must not spend the Open-Meteo quota.
Backend — data & lake backend/data/**, backend/gen_era5_lake.py, backend/gen_cities.py
Backend — migrations backend/alembic/**, backend/alembic.ini One owner, always. Two agents generating revisions produce two heads.
Frontend — server frontend/server/**, frontend/app.py, frontend/api_client.py Go SSR and the Python app.
Frontend — static frontend/static/**
Frontend — templates frontend/templates/**, frontend/content/**, frontend/content.py
Infra — deploy infra/deploy/** (excluding secrets/), infra/docker-compose*.yml
Infra — secrets infra/deploy/secrets/** SOPS vault. One owner, and never alongside infra/deploy.
Infra — ops infra/ops/**, infra/lake-iceberg/**
Infra — terraform infra/terraform/**
Observability observability/** Grafana dashboards are provisioned from this JSON; a UI edit is overwritten.
Docs docs/**
Agent tooling .claude/hooks/**

Serialise — shared spine

Touched by most changes. A change here is its own task with nothing else running against it.

File Why
.forgejo/workflows/** CI lives only here, path-filtered per domain. Every domain's changes route through the same files, so two agents editing workflows conflict even when their domains do not.
CLAUDE.md and every domain CLAUDE.md Read before every change; a stale one is a correctness bug. Small edits, landed fast.
CUTOVER-NOTES.md The source of truth for what is and is not live.
infra/deploy/env-topology.sh The single source of truth for where each environment's checkout, branch, stack and ports live.
infra/deploy/deploy.sh, infra/deploy/stack/deploy-stack.sh The deploy contract's entry points.
backend/CLAUDE.md + the /api/version contract FE and BE ship out of lockstep, so PAYLOAD_VER discipline is load-bearing. A change to the payload shape is one task spanning both domains — not two concurrent ones.

The recurring collisions

  • A schema change is not a backend-only task. It is backend/alembic plus whatever reads the column, and if the payload shape moves it is PAYLOAD_VER and the frontend too. Scope it as one task across both domains rather than two agents discovering each other mid-flight.
  • Adding CI for a domain edits the shared workflows. Sequence it against any other workflow work.
  • infra/deploy/secrets/ and infra/deploy/ are one owner between them. The rendered artifact and the thing that renders it move together.