Skip to content

Development

Local development setup

git clone https://github.com/Garden-AI/rootstock.git
cd rootstock
uv venv && source .venv/bin/activate
uv pip install -e ".[dev]"

Code quality

Run linting before committing:

ruff check rootstock/
ruff format rootstock/

Project structure

rootstock/
├── rootstock/
│   ├── __init__.py
│   ├── calculator.py             # ASE Calculator interface
│   ├── server.py                 # Spawns worker subprocess, manages socket lifecycle
│   ├── worker.py                 # i-PI client state machine
│   ├── environment.py            # Pre-built environment management
│   ├── clusters.py               # Cluster registry and known environments
│   ├── cli.py                    # CLI entry point
│   └── commands/                 # One module per CLI command (install, add, status, ...)
├── sample_model_configurations/  # Sample environment files, grouped by hardware target
├── skill/                        # Agent skill (skill.md)
├── lammps/                       # LAMMPS fix source files
├── tests/
└── docs/

Worker compatibility policy

Every built environment pins its own copy of rootstock, and only the modules on the worker path actually execute inside the env at runtime: worker.py, protocol.py, and worker_config.py (entered through the run_worker() call in the generated wrapper script). Everything else in the pinned package is dead code inside an env. Fixing anything on the worker path after release means rebuilding every environment on every cluster — so treat the worker side as a versioned internal ABI, frozen at 1.0:

  • the wire protocol: the set of 12-byte messages and the exact byte layout of every payload (see the golden test below);
  • the run_worker() call signature as invoked by the wrapper (new options must be optional; workers ignore unknown kwargs);
  • the meaning of the INIT JSON keys (numbers, pbc) and of the FORCEREADY extra field ({"error": ...} objects are worker errors).

A newer client must keep working against every worker ever deployed. The only sanctioned extension channels into old workers are:

  1. The generated wrapper script — regenerated by the client every run; it can probe the env (e.g. importlib.metadata.version("rootstock")) and fail with "rebuild required" before anything touches the socket.
  2. Environment variables set at spawn — the ROOTSTOCK_WORKER_* knobs documented in the architecture page.
  3. New keys in the INIT JSON payload — old workers .get() the keys they know and ignore the rest.
  4. The FORCEREADY extra field (worker → server) — receivers that don't understand a payload discard it.

Never: new message types, changed payload layouts, new required run_worker arguments, or new required INIT keys. If a change can't ride one of the four channels above, it waits for a major protocol revision and a coordinated env rebuild.

The LAMMPS styles are a third frozen peer. rootstock_ipi.cpp (shared by fix rootstock and pair_style rootstock) is an independent implementation of the wire protocol that users compile into their LAMMPS binaries — it has its own freeze horizon, separate from both the PyPI client and the env-pinned worker. A binary built today must keep driving every worker ever deployed, and no client- or worker-side change may assume LAMMPS binaries get rebuilt. Wire-protocol changes must be checked against all three implementations: protocol.py server side, protocol.py worker side, and lammps/rootstock_ipi.cpp.

tests/protocol/test_wire_golden.py pins the exact bytes of every message. If it fails, the change breaks compatibility with every deployed environment — fix the change, not the test. (Post-1.0, a CI job should additionally build an env with rootstock==1.0 and drive it with the HEAD client.)

Running tests

pytest tests/

Get involved

We welcome feedback, bug reports, and collaborators.

For bugs, feature requests, or to contribute an environment file for a new MLIP, open an issue on GitHub.

If you are an HPC admin who would like to support Rootstock on their own cluster or want to discuss a scientific use case, please reach out to Will Engler at willengler@uchicago.edu.