Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Contributing to proef

Small, focused PRs against main. The corpus in docs/ is the source of truth — CLAUDE.md is the working summary, docs/TECH-SPEC.md and the ADRs win on conflict.

Setup

The toolchain is pinned by rust-toolchain.toml (latest stable; rustup picks it up automatically). One-time tools:

cargo install cargo-nextest cargo-deny cargo-audit cargo-insta just
# plus, for the full CI surface locally:
cargo install cargo-fuzz cargo-public-api cargo-machete cargo-llvm-cov   # llvm-cov: `just cover`

Native build prerequisites (only proef-engine-hurl needs them): Debian/Ubuntu apt install build-essential pkg-config libssl-dev libcurl4-openssl-dev libxml2-dev libclang-dev; macOS: Xcode CLT.

The gates (green before every commit)

cargo nextest run                     # all tests
cargo test --doc                      # doctests (nextest skips them)
cargo clippy --all-targets --all-features -- -D warnings
cargo fmt --all --check
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features --workspace
cargo deny check
cargo run -p xtask -- docs-check      # indexes ↔ reality
cargo run -p xtask -- public-api      # proef-core API surface (nightly rustdoc)
cargo check --manifest-path fuzz/Cargo.toml --all-targets --locked   # fuzz/ is its own workspace
cargo machete                         # unused dependencies

just gates runs the set above. CI additionally runs the #[ignore]d complexity guard alone (just perf — TESTING-STRATEGY §7), zizmor, a proef doctor smoke, a fuzz smoke, the hurl canary, a Windows gate, the docs-site build, and — on a pull request — the changelog self-recording check; cargo audit and the full fuzz run nightly.

Rules that are easy to trip over

  • hurl pins are exact (=8.0.1, built --locked). Never bump them in a PR — upgrades go through the canary + runbook (ADR-0003).
  • Snapshots are deliberate. Artifact bytes, sidecars, diagnostics, and event streams are insta-locked; cargo insta review each diff and be able to say why it changed. Never blind-accept.
  • proef-core API is snapshot-locked (crates/proef-core/public-api.txt). An intended surface change regenerates it: PROEF_PUBLIC_API_UPDATE=1 cargo run -p xtask -- public-api.
  • Core purity: proef-core does no IO and reads no clocks/env/randomness — inject values instead. This keeps every snapshot deterministic.
  • New architectural decision → new ADR (docs/adr/ADR-00NN-*.md, next number, same format) in the same PR. Diverging from an ADR without a superseding one is a bug.
  • New diagnostic → index it in docs/DIAGNOSTICS.md, prefer a seeded case under tests/errors/<area>__<name>/ (dry-running that corpus fails by design).
  • YAML is serde_norway, datetime is jiff, and reqwest/async-trait/ a tokio runtime are banned (see CLAUDE.md for the full list and why).
  • No raw print macros in proef-cli. Use crate::render::outln! for stdout and crate::render::errln! for stderr. println!/eprintln! panic when the write fails, and a closed pipe (proef … | head) surfaces as EPIPE rather than a signal — so a raw macro aborts with 101, outside the typed 0/1/2/3 exit contract (ADR-0009). A source-scanning test enforces this.

Testing

docs/TESTING-STRATEGY.md is normative. In short: everything is device- and network-free except the fixture integration suite (cargo run -p xtask -- fixture runs the dev API server standalone). Assert attempt counts and normalized event order, never wall-clock. just cover measures line coverage on demand (cargo-llvm-cov; advisory, never a threshold — TESTING-STRATEGY §3).

Commit messages

Conventional prefixes (fix:, feat:, docs:, refactor:, release:), imperative subject, body explains why. No AI-attribution footers.