Add a `test` workflow that runs `mise run verify` (ruff lint + pytest) on
every push and PR, dogfooding the same verify task the control layer's Stop
hook enforces — giving CI visibility into the suite.
Also clear HANDLER_SECRET_KEY and CLAUDE_BIN in the `env` test fixture. When
the suite runs inside a Handler-managed container these are set in the ambient
environment and leaked into tests asserting the unset behavior (secretstore
refusing without a key; login using the default `claude` binary), which then
failed. Clearing them (as the fixture already does for WEBHOOK_URL) keeps
behavior test-driven, not host-driven.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a second image for the handler control CLI (spawn/list/kill, approve/
reject, forge-init, CI poller) alongside the existing API image. It shares
the package, database, and /var/lib/handler volume but runs the control
process instead of uvicorn.
- Dockerfile.control: git + tmux baked in for live spawning; default CMD is
the `poll-ci --watch` loop; RUN_MIGRATIONS toggle reuses docker-entrypoint.sh.
- docker-control.yml: builds/pushes ghcr.io/<repo>/control (multi-arch),
scoped gha cache so it doesn't clobber the API build.
- docker-compose.yml: new `control` service, RUN_MIGRATIONS=false, depends on
the API (which owns migrations) being healthy.
- README: Containers section documenting both images and compose usage.
Multi-stage Dockerfile (python:3.11-slim, non-root, /health healthcheck)
with an entrypoint that applies alembic migrations before uvicorn, a
compose file pairing the API with Postgres 16, and a CI workflow that
builds multi-arch images and pushes branch/semver/sha/latest tags to
ghcr.io. PR builds compile the image without pushing.