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