| `refactor:` | Code restructuring with no behavior change |
| `test:` | Adding or fixing tests |
| `chore:` | Everything else (e.g. `.gitignore`, version bumps) |
Examples:
```
feat: add S5 domain picker to installer wizard
fix: handle empty lsblk output in drives.parse_size_gb
docs: document NS delegation flow for managed gateway
ci: add ruff format check to Forgejo Actions workflow
```
Keep the subject line under 72 characters. Use the body to explain *why* — the code already says *what*.
## Code style
- **Python:** ruff handles lint + format. Config in `pyproject.toml`. Run `ruff check . && ruff format .` before committing.
- **Markdown:** wrap at a sensible width (no hard line-length rule). Every external link you add to a doc must resolve (the markdown-link-check CI job will catch it if it doesn't).
- **No comments that explain *what*** — the code says that. Only comment the *why* when it's non-obvious.
## PR / push workflow
While the team is two people, pushing to `main` is fine for small changes. For anything larger than ~50 lines or that touches architecture, open a PR and get the other person to look at it.
## Tests
Tests live in `tests/`. Pure functions get unit tests; anything that shells out to `lsblk` / `smartctl` is out of scope (those need hands-on testing on real hardware).
```bash
pytest # all tests
pytest tests/test_drives.py -v
```
## Releases
See [RELEASING.md](RELEASING.md).
## License
AGPL-3.0 (see `LICENSE`). By contributing you agree your contributions are licensed the same way.