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
.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.
60 lines
3.7 KiB
Markdown
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.
|