Run it locally
Prerequisites
Section titled “Prerequisites”- Ruby 3.4.6 (see
.ruby-version) - PostgreSQL 14+
- Redis
- Node.js (for the AIR CLI and
npx-based MCP servers) - The
claudeand/orcodexCLI, logged in — orpi, which takes anOPENROUTER_API_KEYin the environment instead of a login. See Runtimes ghCLI, logged in (agents use it to open PRs)
bundle installcp .env.example .env # then set ANTHROPIC_API_KEYbin/rails db:setupbin/dev # → http://localhost:3000bin/setup does the same thing plus bundle check, db:prepare, and log:clear, then execs
bin/dev. Use PORT=0 bin/dev for a random port.
bin/dev installs foreman if it’s missing, warns (non-fatally) if redis-cli ping fails, kills any
stale foreman or port-3000 processes, and starts Procfile.dev:
web: bin/rails servercss: while true; do bin/rails tailwindcss:watch || sleep 5; doneTwo databases
Section titled “Two databases”config/database.yml and config/cable.yml expect two databases per environment:
zimmer_development and zimmer_development_cable. The second is Action Cable’s, via
solid_cable. bin/rails db:setup creates both.
Environment variables
Section titled “Environment variables”Everything in .env.example is commented out except RAILS_ENV=development. The ones that matter:
| Var | What for |
|---|---|
DATABASE_HOST / _PORT / _USERNAME / _PASSWORD | Postgres. Postgres.app users want 5450. |
REDIS_URL | Cache. redis://localhost:6379 — the server, with no database index. Each environment picks its own (development’s cache takes 1, production 0). |
ANTHROPIC_API_KEY | Claude Code, if not using OAuth |
API_KEYS | Comma-separated keys for the REST API. Unset ⇒ the API 401s on everything. |
APP_HOST | The MCP OAuth redirect host. Unset ⇒ defaults to localhost:3000. |
RAILS_MASTER_KEY | Unlocks Rails credentials (mcp_secrets, mcp_oauth_clients) |
AIR_CONFIG | Override which air.json the catalog resolves from |
AGENT_CLONES_DIR | Where session clones go. Default ~/.zimmer/clones |
GOOD_JOB_AGENTS_THREADS | Worker concurrency: how many agent sessions run at once. It also moves the database pool that has to serve them — see the connection budget |
GOOD_JOB_MAINTENANCE_THREADS | Worker concurrency for blocking package, archive, and filesystem maintenance. It also moves the database pool. |
gh and the agent CLIs authenticate via OAuth (gh auth login, claude auth login), not env vars.
First run: the catalog
Section titled “First run: the catalog”On boot, config/initializers/air_catalog.rb runs AirCatalogService.refresh!, which lazily
npm installs the AIR CLI (pinned to 0.13.0) into AIR_INSTALL_DIR and then shells out to
air resolve. The first boot is slow because of that install.
AIR_INSTALL_DIR defaults to /opt/air-cli where that exists (the Docker image bakes it there)
and ~/.cache/air-cli otherwise. The test environment is deliberately separate — always
~/.cache/air-cli-test — because test/test_helper.rb installs the CLI at suite boot, and on a
host that is also running Zimmer that would reinstall over the running app’s copy. Set the variable
explicitly to override either.
If the catalog fails to resolve, the app downgrades to a warning and serves a stale snapshot — but the test suite is less forgiving. See below.
Running tests
Section titled “Running tests”bin/rails test test/models/session_test.rb # targeted — do thisbin/rails test # the whole suitebin/rubocop # lintbin/brakeman # security scanRun targeted tests locally and let CI run the full suite.
The browser suite runs in CI
Section titled “The browser suite runs in CI”.github/workflows/ci.yml runs the Chrome-driven system suite (test/system/*.rb) in a dedicated
test-system job, alongside the unit + integration test-unit job. The JavaScript Playwright
scripts under test/e2e/*.js are the one browser layer still not wired in (issue #162) — see
Testing philosophy.