This file provides guidance to coding agents when working with code in this repository.
AI tools may assist with implementation, but do not add Claude or another AI tool as a commit collaborator, co-author, or signatory. Commit sign-off belongs to the human contributor responsible for the change.
- Rust stable 1.96+
- Rust nightly (for
rustfmt) - Docker 29.3.0+ or Podman (for container builds)
make setup-hooks # install git pre-commit hook
make build # workspace build
make test # all tests
make fmt # format with nightly rustfmt
make lint # clippy + nightly fmt check
make lint-extra # typos + taplo + shellcheck
make doc # rustdoc with -D warnings
make audit # cargo audit + cargo deny check
make coverage-check # fail if line coverage < 95%
make container # container image buildRun a single test:
cargo test -p praxis-operator -- test_nameThree-controller design managing Gateway API resources:
GatewayClass Controller -> accepts/rejects GatewayClasses
Gateway Controller -> reconciles Gateways (primary)
HTTPRoute Controller -> updates route status
Gateway controller reconciliation flow:
- Verify GatewayClass ownership
- Collect attached HTTPRoutes
- Convert Gateway listeners to Praxis config
- Convert HTTPRoute rules to Praxis routing config
- Assemble full Praxis YAML configuration
- Apply ConfigMap, Deployment, Service via SSA
- Update Gateway status conditions
Module structure:
controller/- reconciliation loopsgateway_api/- attachment, conditions, validationconfig/- Praxis YAML generationresources/- K8s resource buildersendpoints.rs- EndpointSlice resolutionstores.rs- reflector-backed cachesleader.rs- leader election via Leaseobservability/- metrics and health endpoints
Full conventions in docs/conventions.md.
Project-specific additions beyond the user-level
Rust Baseline:
- Controller pattern:
reconcile+error_policy, finalizers for cleanup, owner references for GC, server-side apply for all mutations - Use enums for fixed value sets in config, not
strings;
#[serde(deny_unknown_fields)]on config structs;#[serde(try_from)]for constrained numerics;#[serde(default)]instead ofOption<T>withunwrap_or #[expect(clippy::..., reason = "...")]for lint suppression (never bare#[allow])- Descriptive lifetime names (
'route,'listen,'cond) and closure parameters (no single-char identifiers)
New capabilities require:
- Unit tests covering the logic
- Integration tests in
tests/integration.rs - Assertion messages on all
assert!/assert_eq!
30-line threshold enforced by clippy.toml. Do not
suppress too_many_lines in production code; extract
helpers instead. Suppression is OK in test modules.