Contributing to lely¶
Issues and pull requests are welcome.
lely is alpha: everything in the specs is built, and it has run for real only a few
times. spec/ says what each piece must deliver, docs/DESIGN.md how it is built. If the
code disagrees with either, that is a bug in one of them — please say which.
How a change lands¶
Every change is a pull request against main, merged by a maintainer once its checks pass.
ci— lint, format, types, and the tests on Python 3.11–3.14; and once more on 3.11 with every dependency at the oldest versionpyproject.tomlallows. Required.- Small commits with conventional messages. New behaviour comes with a test; a fix comes with the test that would have caught it.
- A change to what lely shows, asks or refuses updates the spec's "As built" and, where a
user would notice,
CHANGELOG.md.
Some parts decide what may run, or handle what isn't trusted: approval.py, running.py,
source.py, planfile.py, cli.py, github.py, and the renderers. A change there is
reviewed by someone who didn't write it before it is opened as a pull request — in this
project's short history, most defects in those files came in with the fix for another.
Toolchain¶
mise pins the tools, and the stack is Astral's:
uv, ruff and
ty.
mise install # the pinned Python and uv (optional but recommended)
uv sync # .venv with dependencies and dev tools
mise run check # the gate: lint, format check, types, unit tests
Day to day¶
uv run lely # the CLI
uv run pytest tests/unit # the unit tests: no workspace, no network
uv run pytest tests/browser # a plugin's view, as Chrome reads it (needs Chrome)
uv run ruff check . && uv run ruff format .
uv run ty check
mise run docs # the docs site, at localhost:8000
ruff format also formats the Python in Markdown code blocks, so the gate covers docs/ and
spec/. When you chain the gate in a shell, a pipe hides a failing test: read the test
count, not the exit code of tail.
The unit suite never reaches a workspace or GitHub: tests/fake_databricks.py and
tests/fake_github.py stand in, and simulate what lely believes those do. What has been
seen on the real things is written down in spec/004-asset-bundle.md and docs/GITHUB.md;
a new assumption about either gets a test, a link to the docs it rests on, and — until it has
been seen — a TODO(verify).
Rules the code keeps¶
CLAUDE.md lists them. The ones a contributor trips over first:
- The core does no I/O of its own. I/O lives at the edges: reading the config file, loading
a plugin, the plugins themselves, the Databricks CLI runner,
git, GitHub, the command line. - No module outside
src/lely/steps/imports a plugin or asks which plugin a step uses — the bundle included. - Nothing that changes a workspace runs unasked, and nothing destructive without
--allow-destructive. - Nothing a plan says is obeyed where it is shown — a terminal, Markdown, the page.
- A plan file carries no secret.
Releases¶
A release is a version, the same way as for stevin and caland: a pull request bumps
version in pyproject.toml and moves the changelog's Unreleased notes under a
## [X.Y.Z] - <date> heading, and merging it is the release. .github/workflows/release.yml
notices the new version on main, runs the gate, publishes to PyPI with Trusted Publishing —
no token — and makes the GitHub release and its tag. A change to pyproject.toml that isn't a
new version releases nothing.
The first release was 0.1.0, on 2026-10-07; CHANGELOG.md has every one since.