---
name: "local_dev_environment"
description: "Build a one-command local dev setup: an idempotent bootstrap script, dotfile hygiene, containerized services with Docker Compose, secrets handling with .env.example, and onboarding docs that get a new person productive in an hour. Use when setting up a repo's dev environment, fixing 'works on my machine', onboarding new developers, or auditing a project's local setup."
---

# Local Dev Environment

## Purpose

Turn a repo's setup from folklore into machinery: one command takes a fresh machine
from zero to running app with tests green, and docs carry a new person to their
first real commit in under an hour. The skill covers four pieces that have to work
together: the bootstrap script, dotfile/toolchain hygiene, containerized services,
and secrets. The output is verifiable: every step ends with a check you can run,
and the whole thing must pass `bin/env_audit.py` before you call it done.

## Workflow

**Step 1 — Inventory.** Before writing anything, list what a developer actually
needs on day one: language runtimes and versions, backing services (database,
cache, queue), secrets and credentials, editor config, and the commands for run,
test, lint, and seed. If you cannot list it, you cannot bootstrap it. See
`references/inventory-worksheet.md`.

**Step 2 — One-command bootstrap.** Write `script/bootstrap.sh` (template in
`assets/bootstrap.sh`). It must be idempotent: safe to run twice, installing only
what is missing, and it must fail loudly on the first problem instead of limping
on. Structure, in order:

1. Detect the OS and package manager; install the missing pieces (git, curl, docker).
2. Install the toolchain through a version manager (mise or equivalent) pinned in
   `.tool-versions` or the devcontainer definition. Never rely on the machine's
   ambient runtime.
3. Apply dotfiles. The repo owns its dotfiles (chezmoi or stow), and the golden
   rule is: never edit a managed file directly on your machine; change the repo
   source and re-apply. See `references/dotfile-hygiene.md`.
4. Clone or enter the project repo, copy `.env.example` to `.env` if missing, and
   run the secrets generator for anything that needs a real value.
5. Bring up containerized services, wait for health, run migrations/seeds.
6. Run the test suite. If tests do not pass at the end of bootstrap, the bootstrap
   is broken, not the tests.

Keep the script short: anything over ~120 lines becomes `script/lib/` helpers or a
devcontainer definition.

**Step 3 — Containerized services.** Provide `compose.yaml` for every backing
service (template in `assets/compose-dev-stack.yaml`):

- Pin every image tag (`postgres:16-alpine`), never `latest`.
- Add healthchecks to stateful services and use
  `depends_on: {condition: service_healthy}` so the app starts after its database
  is ready, not just after the container exists.
- Named volumes for data that must survive restarts; bind mounts for code that
  needs hot reload in development.
- Split dev from the base: keep `compose.yaml` minimal and put hot-reload mounts,
  debug ports, and dev Dockerfiles in `compose.override.yml`, which Compose loads
  automatically.
- Local credentials stay fake, documented, and dev-scoped. Real secrets come from
  `.env`, which is gitignored. Never put a real password in a compose file.
- A new developer should need only `docker compose up -d` to get every service.

**Step 4 — Secrets hygiene.** `.env` holds real values and is never committed;
`.env.example` is committed and documents every variable the project expects, with
placeholder values that say how to obtain or generate the real one
(`JWT_SECRET=generate-with-openssl-rand-base64-48`). Ship
`script/generate-secrets.sh` (template in `assets/`) using `openssl rand` so
developers mint their own values instead of asking in chat. Add a pre-commit hook
that refuses any commit containing `.env`. See `references/secrets-hygiene.md`.

**Step 5 — Onboarding docs.** Write `docs/ONBOARDING.md` as a path, not a
reference manual (template in `references/onboarding-checklist.md`). The target:
a new person runs one command, gets the app running, reads three short docs, and
merges a trivial first PR (even a README fix) by end of day one. Every step is a
checkbox with a verification, not a paragraph. Include a "stuck" rule: after 20
minutes stuck, ask someone. Schedule the doc to be tested: once a quarter, have
someone who never used it follow it exactly on a clean machine and fix every
surprise.

**Step 6 — Verify.** Run `bin/env_audit.py <repo-root>` (passes if every check
below holds). Then dry-run the bootstrap on a clean checkout or a throwaway
container and confirm the app starts and tests pass.

## Output Contract

A finished local dev environment contains:

- `script/bootstrap.sh`: idempotent, fails fast, ends with green tests.
- `compose.yaml` (+ optional `compose.override.yml`): pinned tags, healthchecks,
  named volumes, fake dev-only credentials.
- `.env.example`: every variable documented with placeholder values; `.env`
  gitignored; `script/generate-secrets.sh` committed.
- `.pre-commit-config.yaml`: formatters/linters plus a hook that blocks `.env`.
- `docs/ONBOARDING.md`: day-one path ending in a merged first PR.
- `bin/env_audit.py` output: all checks PASS.

Verification evidence: the audit output plus a log of the clean-machine bootstrap
run. "It worked on my laptop" is not evidence; a fresh run is.

## Operating Rules

1. Idempotency is mandatory: every install step checks before acting, so the
   script can be re-run after a failure without damage.
2. Pin everything: image tags, toolchain versions, extension versions. Drift is
   the enemy; "latest" is never a version.
3. Fail fast and loud. `set -euo pipefail` in every shell script; check
   prerequisites first and name the missing piece in the error message.
4. No real secrets in the repo, ever. Not in compose files, not in scripts, not
   in docs. `.env` lives only on developer machines.
5. The bootstrap must be exercised on a clean environment at least once before it
   ships. Cache staleness hides real breakage.
6. Keep the golden rule of dotfiles: managed files are generated from the repo;
   editing them in place is a guaranteed surprise later.
7. Docs are tested like code: an untested onboarding doc is a draft, not a doc.
8. Compose is the dev and test model. Do not stretch it into the production
   deployment model unless the repo already treats it that way.
