Development
Local setup
git clone https://github.com/AInvirion/ctrlrelay.git
cd ctrlrelay
# Editable install with dev extras (uv recommended):
uv pip install -e '.[dev]'
# Or with pip:
pip install -e '.[dev]'
This installs pytest, pytest-cov, pytest-asyncio, and ruff alongside
the runtime dependencies.
Confirm the CLI is wired up:
ctrlrelay --version
Running tests
# All tests:
pytest
# Verbose, with names:
pytest -v
# A single file:
pytest tests/test_dev_pipeline.py -v
# A single test:
pytest tests/test_dev_pipeline.py::test_blocked_then_resume -v
# With coverage:
pytest --cov=src/ctrlrelay --cov-report=term-missing
pyproject.toml sets pythonpath = ["src"] and testpaths = ["tests"] so
pytest finds the package without an extra install step on a fresh checkout.
The Telegram bridge tests stub the network — no real bot token is required to
run the suite. Tests that exercise the dev / secops pipelines use the
file_mock transport so they stay hermetic.
Linting
# Check:
ruff check src tests
# Auto-fix what's safe:
ruff check --fix src tests
# Format (imports, whitespace):
ruff format src tests
The ruff config in pyproject.toml:
- Line length: 100
- Target: Python 3.12
- Selected rules:
E,F,I,N,W
Both linting and tests should pass before opening a PR.
Validating docs
The docs site under docs/ is a Jekyll build with
the just-the-docs remote theme. There is a
structural test suite that catches misconfigured pages before GitHub Pages
builds them:
pytest tests/test_docs_site.py -v
It checks:
_config.ymlis valid YAML and declares theremote_theme.- Every Markdown page has
titlefront matter. - Every page declared as
parent: Xexists withhas_children: true. - Sibling pages have unique
nav_order.
You can also build the site locally if you have Ruby installed:
cd docs
bundle install # one-time
bundle exec jekyll serve
# Open http://localhost:4000/ctrlrelay/
Project layout
ctrlrelay/
├── src/ctrlrelay/ # Python package (orchestrator core)
│ ├── cli.py # Typer CLI entry point
│ ├── core/ # Dispatcher, worktree, state, config, ...
│ ├── pipelines/ # dev, secops, post_merge
│ ├── bridge/ # Telegram bridge daemon
│ ├── transports/ # SocketTransport, FileMockTransport
│ └── dashboard/ # Optional dashboard push client
├── tests/ # pytest suites (one file per module)
├── config/ # Example orchestrator.yaml
├── docs/ # This Jekyll site
├── scripts/ # Shell helpers (./sync wrapper, manifest)
├── claude-config/ # Git-tracked Claude Code config (export/import)
├── codex-config/ # Git-tracked Codex CLI config
└── mcp-servers/ # MCP servers (codex-reviewer, ...)
Contributing
- Open an issue first for anything beyond a typo or trivial fix. The
ctrlrelaypoller can pick assigned issues up automatically — see Getting started. - Branch naming. Use
fix/issue-{n}(the dev-pipeline default) so the issue ↔ branch ↔ PR linkage stays clean. - TDD. Write or extend tests for any behavior change. Pipelines have
integration tests under
tests/test_*_pipeline.py; the bridge has its own asyncio-based suite. - Lint and test before pushing. CI runs both; pre-push catches things faster.
- Keep PRs small. A single PR per logical change makes review tractable and lets the post-merge handler keep the issue / PR / branch lifecycle clean.
- Don’t commit secrets. Bot tokens and dashboard tokens belong in
environment variables —
transport.telegram.bot_token_envanddashboard.auth_token_envonly hold the variable name.
CI
Two GitHub Actions workflows live under .github/workflows/:
build.yml— runs on push tomain, on tag pushes (v*), and on PRs. Builds the sdist and wheel viauv build. On tags / releases, attaches the artifacts to the GitHub release.pages.yml— runs on push tomainwhen anything underdocs/changes. Builds the Jekyll site and deploys to GitHub Pages.
Both must pass before a PR is mergeable.