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

60 lines
3.7 KiB
Markdown

# 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.