This document captures code conventions for the projects in this folder. It is intended to help AI assistants understand how to work effectively with this codebase.
For the .SPZ file format specification and details see ./docs/SPZ.md.
- Model the full error space—no shortcuts or simplified error handling.
- Handle all edge cases, including race conditions, signal timing, and platform differences.
- Use the type system to encode correctness constraints.
- Prefer compile-time guarantees over runtime checks where possible.
- Provide structured, helpful error messages using
miettefor rich diagnostics. - Make progress reporting responsive and informative.
- Maintain consistency across platforms even when underlying OS capabilities differ.
- Use OS-native logic rather than trying to emulate Unix on Windows or vice versa.
- Write user-facing messages in clear, present tense: "spz now supports..." not "spz now supported..."
- "Not overly generic"—prefer specific, composable logic over abstract frameworks.
- Evolve the design incrementally rather than attempting perfect upfront architecture.
- Use type system extensively: newtypes, builder patterns, type states, lifetimes.
- Use message passing or the actor model to avoid data races.
- Comprehensive testing including edge cases, race conditions, and stress tests.
- Getting the details right is really important!
- Use inline comments to explain "why," not just "what".
- Module-level documentation should explain purpose and responsibilities.
- For rust docstrings, instead of
# Argumentsuse# Args.
- Every Rust source file must start with:
// SPDX-License-Identifier: MIT OR Apache-2.0- Every Python source file must start with:
# SPDX-License-Identifier: MIT OR Apache-2.0- Always have an empty line after the license line.
- Use Rust 2024 edition.
- Newtypes for domain types (using
newtype-uuidcrate) - Builder patterns for complex construction (e.g.,
TestRunnerBuilder) - Type states encoded in generics when state transitions matter
- Lifetimes used extensively to avoid cloning (e.g.,
TestInstance<'a>) - Restricted visibility: Use
pub(crate)andpub(super)liberally - Non-exhaustive: All public error types should be
#[non_exhaustive]for forward compatibility
- Use
thiserrorfor error types with#[derive(Error)]. - Group errors by category with an
ErrorKindenum when appropriate. - Provide rich error context using
miettefor user-facing errors. - Two-tier error model:
ExpectedError: User/external errors with semantic exit codes.- Internal errors: Programming errors that may panic or use internal error types.
- Error display messages should be lowercase sentence fragments suitable for "failed to {error}".
- Use
tokiofor async runtime (multi-threaded). - Be selective with async. Only use it in runner and runner-adjacent code.
- Use async for I/O and concurrency, keep other code synchronous.
- Use
async-scopedfor structured concurrency without'staticbounds. - Use
future-queuefor backpressure-aware task scheduling. - Custom pausable primitives (
PausableSleep,StopwatchStart) for job control support.
- Use
mod.rsfiles to re-export public items. - Do not put any nontrivial logic in
mod.rs. - Keep module boundaries strict with restricted visibility.
- Platform-specific code in separate files:
unix.rs,windows.rs. - Use
#[cfg(unix)]and#[cfg(windows)]for conditional compilation. - Test helpers in dedicated modules/files.
- Use
Arcor borrows for shared immutable data. - Use
smol_strfor efficient small string storage. - Careful attention to copying vs. referencing.
- Use
debug-ignoreto avoid expensive debug formatting in hot paths. - Stream data where possible rather than buffering.
- Always use an underscore when suffixing an integer with it's type: example:
- DO NOT use
0u32 - DO USE
0_u32
- DO NOT use
CRITICAL: Always use cargo nextest run to run unit and integration tests.
Never use cargo test for these!
For doctests, use cargo test --doc.
- Unit tests in the same file as the code they test.
- Integration tests in
tests/integrationcrate. - Test utilities in
tests/utilitiescrate.
- test-case: For parameterized tests.
- proptest: For property-based testing.
- insta: For snapshot testing.
- libtest-mimic: For custom test harnesses.
- pretty_assertions: For better assertion output.
Commits follow a conventional format with crate-specific scoping:
[crate-name] brief description (#PR-number)
Examples:
[nextest-runner] add --max-progress-running, cap to 8 by default (#2727)[cargo-nextest] version 0.9.111[meta] update MSRV to Rust 1.88 (#2725)
- Use
[meta]for cross-cutting concerns (MSRV updates, releases, CI changes) - Version bump commits:
[crate-name] version X.Y.Z(these are performed bycargo release) - Release preparation:
[meta] prepare releases - Include PR number for all non-version commits
- Use lowercase for descriptions
- Keep descriptions concise but descriptive
- Atomic commits: Each commit should be a logical unit of change
- Bisect-able history: Every commit must build and pass all checks
- Separate concerns: Don't mix formatting fixes with logic changes
- Format fixes and refactoring should be in separate commits from feature changes
- No direct state sharing—everything via message passing
- Linearized events—dispatcher ensures consistent view
- Full error space modeling—handle all failure modes
- Pausable timers—custom implementations for job control (SIGTSTP/SIGCONT)
- Structured concurrency—use
async-scopedfor spawning with borrows
- Unix: Process groups, double-spawn pattern to avoid SIGTSTP race, full signal handling.
- Windows: Job objects, console mode manipulation, limited signal support.
- Conditional compilation:
#[cfg(unix)],#[cfg(windows)]. - Platform modules:
unix.rs,windows.rswith shared interfaces. - Document platform differences and trade-offs in code comments.
- All versions managed in root
Cargo.toml[workspace.dependencies] - Internal crates use exact version pinning:
version = "=0.17.0" - Comment on dependency choices when non-obvious
- Example: "Disable punycode parsing since we only access well-known domains"
- tokio: Async runtime, essential for concurrency model
- anyhow: Error helper crate
- thiserror: Error derive macros
- serde: Serialization (config, metadata)
- clap: CLI parsing with derives
# to interact with python or pip, ALWAYS use uv or uvx
uvx -p crates/spz-binding-python/.venv python
uvx -p crates/spz-binding-python/.venv pip
# Run tests (ALWAYS use nextest for unit/integration tests)
just test
# Run doctests (nextest doesn't support these)
cargo test --doc
# Lint
just lint
# Build
just build
# Release
just build-release# Get commits since last release
git log <previous-tag>..main --oneline
# Check if contributor is first-time
git log --all --author="Name" --oneline | wc -l
# Get PR author username
gh pr view <number> --json author --jq '.author.login'
# View commit details
git show <commit> --stat