Run targeted subsets locally, not the whole suite — the full suite is slow everywhere and CI owns it. There are two compose files, for two different jobs:
docker-compose.dev.yml— the fast iterate loop. It bind-mounts your working tree, so you edit a.rbfile and re-run a targeted test with no rebuild. This is the day-to-day one.docker-compose.test.yml— reproduces CI exactly (bakes the code into the image the way the Dockerfile and CI do). Reach for it only to confirm "does this behave the same as CI"; it needs a rebuild after every code change.
One-time setup (Postgres + Redis stay up across runs):
docker compose -f docker-compose.dev.yml up -d db redis
docker compose -f docker-compose.dev.yml run --rm dev bin/rails db:test:prepareThen iterate — edit code and re-run a targeted subset, no rebuild, no re-prepare:
docker compose -f docker-compose.dev.yml run --rm dev bin/rails test test/models/user_test.rb
docker compose -f docker-compose.dev.yml run --rm dev bin/rails test test/models -n /handle/
docker compose -f docker-compose.dev.yml run --rm dev bash # a shell in the stackThe working tree is live-mounted, so any app/ or test/ edit is picked up
immediately. Rebuild only when Gemfile.lock or package.json changes:
docker compose -f docker-compose.dev.yml build
docker compose -f docker-compose.dev.yml down -v # refresh the dep + db volumesdocker compose -f docker-compose.test.yml build
docker compose -f docker-compose.test.yml run --rm test bash -c "bin/rails db:prepare && bundle exec rails test test/models"
docker compose -f docker-compose.test.yml down -v # tear down + drop the db volumeThe image bakes the app code, gems and assets, so rebuild after changing code — that's the price of matching CI's fresh-checkout build exactly.
The TS tests are plain Node (v22) and need no Postgres, Redis, or containers —
they run natively in seconds. This mirrors CI's separate typescript job, which
covers three independent packages:
# Frontend UI (root package.json) — Stimulus controllers etc., vitest + jsdom
npm install
npm run typecheck # tsc --noEmit
npm test # vitest run (app/javascript/**/*.test.ts)
npm run build # esbuild bundle
# agent-runner (vitest)
cd agent-runner && npm install && npm run build && npm run typecheck && npm test
# harmonic-bridge (node's built-in --test runner)
cd harmonic-bridge && npm install && npm run typecheck && npm test && npm run buildSo frontend Stimulus work, agent-runner, and harmonic-bridge need neither
Docker nor a database — just Node and disk for node_modules.
Playwright e2e is the one TS surface that needs a running app — it drives a
real Chromium against the stack (web + db + redis up), reachable at
E2E_BASE_URL (default https://app.harmonic.local) with the authed storage
state from e2e/global-setup.ts.
npm run playwright:install # one-time: install the Chromium browser
npm run test:e2e # playwright test (needs the app running)CI does not run Playwright in the typescript job — it's for local/manual use.