Your first PR
The 30-minute path: clone, run make check green, pick something small, ship it. Six checkboxes; the rest of Contributing is for once you’re past these.
The path
-
git clonethe repo.git clone git@github.com:zebbra/remote-lab.git. Branch fromdevelop, notmain— every PR targetsdevelop. - Read
AGENTS.md. One screen. Lists the conventions code review enforces —*Dtosuffix,_run_blocking()for blocking I/O in async handlers,connector.run_netlab()as the only Netlab path, CVE-pinned deps. Skim this before your first commit. - Run
make check. Green on a fresh clone means your dev environment is set up. If it isn’t, Dev setup walks the diagnose-and-fix path. Do not skip this step — you need it green to know whether your changes broke something. - Read Invariants. The eight rules a PR cannot violate. Each entry is 5–10 lines. Read all eight; the one that bites you on your first PR is rarely the one you’d guess.
- Pick a good-first-issue. Or a typo, or a doc fix, or anything you’ve already noticed while reading these docs. The first PR is about the flow, not about a heroic feature.
- Open a PR. CI runs
make check(lint + typecheck + tests). When it’s green, ask for review.
What CI is going to check
make check runs four things in sequence. If your local pre-push run is green, CI’s run will be too.
make lint # ruff format-check + ruff check
make typeCheck # pyrefly
make test # pytest -q (39 tests today)
make audit # pip-audit --strict
If make audit fails on a pre-existing CVE
make audit failing on a transitive CVE you didn’t introduce is a known state — track separately, don’t block your PR on it. The maintainer pins these explicitly with # CVE-* comments in pyproject.toml. See Invariants → CVE-pinned dependencies.
Where to go next based on what you’re touching
-
make checknot green? The dev-setup page walks the install + diagnose + fix path. -
The eight load-bearing rules. Re-read the relevant one before any non-trivial change.
-
Touching
server.pyand adding a new operation that calls Netlab? Read this first — the_run_blockingdiscipline is non-obvious. -
Touching
lab_manager.pyorconnector.py? Read this.try_acquirevsacquireis the easiest way to wedge the test suite. -
Touching teardown? Read this — async in
atexitdeadlocks the interpreter. -
Adding a test that needs
LabManagerbut CI has nonetlab? Subclass and override.
After your first PR is in, Anti-patterns is the one-page grep target you’ll want bookmarked for review.
See also
AGENTS.md— the same invariants in repo-root form, alongside agent-bootstrap context. Read this if you’re working with an AI assistant.README.md— project overview, install, quick orientation.- Architecture — the high-level picture. The Internals pages assume you’ve read this.