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.
3.7 KiB
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/alembicplus whatever reads the column, and if the payload shape moves it isPAYLOAD_VERand 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/andinfra/deploy/are one owner between them. The rendered artifact and the thing that renders it move together.