7a475a52fb
Direct `docker compose -f infra/local/dev.compose.yml ...` works fine, but two things kept biting on this stack: - Compose-profile asymmetry — `down` only operates on services whose profile is currently active, so anything brought up with `--profile X` keeps running unless the same flag is on `down`. pgweb and Jaeger silently survived several `down -v` invocations before we spotted it. - Verbose invocations — typing the compose-file flag and the profile flags on every command for routine ops gets old fast. Add `infra/local/dev.sh` as a thin wrapper: ./infra/local/dev.sh up # core only ./infra/local/dev.sh up all # core + every profile ./infra/local/dev.sh up dbtools # core + pgweb ./infra/local/dev.sh up observability # core + Jaeger ./infra/local/dev.sh down [-v] # always with all profiles ./infra/local/dev.sh stop <service> # stop one service ./infra/local/dev.sh restart <service> # restart one service ./infra/local/dev.sh status # ps with all profiles ./infra/local/dev.sh logs [service] # follow logs ./infra/local/dev.sh exec <svc> <cmd> # run inside a container Anything not matching one of the named verbs is passed through to `docker compose -f dev.compose.yml ...` (with every profile flagged in), so the full Compose surface remains available without losing the profile-symmetry guarantee. Docs: - `infra/README.md` "Local-dev stack" — new "Convenience script" subsection with the cheat-sheet table; "First-time setup" walk- through rewritten to use the script; the previous standalone "Profiles must match on `down` as on `up`" tip is collapsed into a one-liner since the script handles it. - `docs/development.md` §3 — point at the script for the typical setup flow. The compose file itself is unchanged.