Contributing

Contributions are welcome. Please read this guide before opening a pull request.

Code of Conduct

All participants are expected to follow the Code of Conduct.

Licensing

Gradient is licensed under AGPL-3.0-only. By submitting a contribution you agree that your work will be released under the same license. All files must carry an SPDX header:

// SPDX-FileCopyrightText: 2026 Wavelens GmbH <info@wavelens.io>
//
// SPDX-License-Identifier: AGPL-3.0-only

Development Setup

Prerequisites: Nix with flakes enabled.

# Backend
nix run .#backend
> run_tests()

cd backend
cargo run

# Tip: parallel `rustc` jobs are capped at 2 in `backend/.cargo/config.toml`
# (`[build] jobs = 2`) to keep peak memory bounded on dev machines. Override
# with `cargo build -j N` if you have more headroom.

# Frontend
nix run .#frontend
> run_tests()

cd frontend
pnpm install
pnpm run serve

The frontend VM provisions a superuser admin (password admin_password), an organization, a project, and an in-VM worker via declarative state, so evaluations and builds run end-to-end against pnpm run serve.

Integration Tests

NixOS VM tests:

nix build .#checks.x86_64-linux.gradient-api       -L
nix build .#checks.x86_64-linux.gradient-state     -L
nix build .#checks.x86_64-linux.gradient-cache     -L
nix build .#checks.x86_64-linux.gradient-building  -L
nix build .#checks.x86_64-linux.gradient-oidc      -L
nix build .#checks.x86_64-linux.gradient-remote    -L

Workflow

  1. Open an issue to discuss the change before significant effort.
  2. Fork and create a feature branch from main.
  3. Implement with tests where applicable.
  4. Open a pull request against main.

Code Conventions

Rust

  • Format with cargo fmt before committing.
  • No unwrap() in production paths - use ? or explicit error handling.
  • New API endpoints go in web/src/endpoints/ following the pattern: extract path/query params → check authorization → query DB → return response.
  • New database tables require a migration in migration/src/ and an entity module in entity/src/.
  • Log with tracing::{info, debug, warn, error}, not println!. Add #[instrument] to significant async functions.
  • Update docs/gradient-api.yaml whenever an API endpoint is added or changed.
  • Update environment variable documentation and the corresponding nix/modules/ files when configuration options change.

Toolchain, formatting and lints

The toolchain is pinned in rust-toolchain.toml (rustup) and mirrored by the nix devShell (flake.lock), which stays the source of truth. Formatting is pinned via backend/rustfmt.toml (style_edition = "2024"), so cargo fmt is reproducible across rustfmt versions.

Run before pushing (matches CI):

cd backend
cargo fmt --all --check
cargo deny check                            # license/advisory policy - GPL-family deps are banned
nix build .#checks.x86_64-linux.clippy -L   # cargo clippy --workspace --all-targets -- -D warnings

#[allow] policy:

  • #[allow(unused_imports)], #[allow(unused)] and #[allow(dead_code)] are forbidden (CI grep-gate) - fix the underlying warning instead of silencing it.
  • Every other #[allow(...)] must carry a reason = "..." (clippy::allow_attributes_without_reason). clippy::too_many_arguments allows are temporary and tracked in #503.

CI (.github/workflows/rust.yml) runs fmt, the grep-gate and cargo-deny; clippy runs as the checks.clippy flake check.

Angular / TypeScript

  • Standalone components with Angular signals (signal(), computed()).
  • Feature-based structure under frontend/src/app/features/.
  • PrimeNG for UI components; SCSS variables from src/app/styles/_variables.scss for colours and spacing.

Nix

  • All packages and modules live in nix/.
  • Server options go in nix/modules/gradient.nix; worker options go in nix/modules/gradient-worker.nix.
  • New modules need a NixOS VM test in nix/tests/.