Thank you for your interest in contributing to soothfast. This document covers everything you need to get started.
- Code of Conduct
- Reporting Bugs
- Reporting Security Vulnerabilities
- Suggesting Features
- Development Setup
- Building and Testing
- The Dogfood Gate
- Code Style
- Dependency Policy
- Submitting a Pull Request
- Commit Message Format
- Release Process
This project follows the Contributor Covenant Code of Conduct. By participating you agree to abide by its terms. Report violations to harveytseng2@gmail.com.
Before filing a bug, search existing issues to avoid duplicates.
Open a new issue and include:
- The crate affected (
soothfast,soothfast-macros,soothfast-registry,soothfast-measure,soothfast-docs,soothfast-spec,soothfast-sdk,soothfast-report,soothfast-site,cargo-soothfast). - The version (from
Cargo.tomlorcargo soothfast --version). - Steps to reproduce, including a minimal code example where possible.
- Actual vs. expected behavior.
- Rust version (
rustc --version) and OS. For measurement issues, note whetherperf_event_openis available (bare metal vs. container vs. CI runner) and which backend was selected.
Do not open a public issue for security vulnerabilities. See SECURITY.md for the private disclosure process.
Open an issue with the enhancement label. Describe:
- The problem you are trying to solve.
- Your proposed solution and any alternatives you considered.
- Whether you are willing to implement it (helps prioritize).
For large changes (new metric backends, new spec providers, new public API surface), open an issue for discussion before writing code — this avoids wasted effort if the direction does not fit the project.
Prerequisites:
- Rust 1.85 or later (the workspace MSRV) — install via rustup
valgrind(optional, for the callgrind measurement backend on machines withoutperf_event_openaccess)- A nightly toolchain (optional, only for the rustdoc-JSON docs engine:
rustup toolchain install nightly)
Clone and build:
git clone https://github.com/Verdenroz/soothfast.git
cd soothfast
cargo build --workspaceInstall git hooks (optional but recommended):
pre-commit install # or: prek installmake help # list all targets
make check # fmt + clippy + tests (same as CI's check job)
make baselines # measure the self-bench crates into the shared baseline
make ci # everything CI runs, minus the PR gates
make gate # all merge-base gates (perf + build cost)
make docs # regenerate every site page, then serve the docs site locally with live reloadOr directly with Cargo:
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo fmt --all -- --checkAll tests must pass before a PR is merged.
soothfast gates its own performance with itself. Every PR runs the regression
gate against the merge-base of the PR branch and master: the base revision is
measured in a temporary git worktree with interleaved rounds, so there are no
committed baselines to go stale.
Run it locally before pushing:
make gate # all gates against the origin/master merge-base
make gate BASE=master # or against a local refIf the gate fails in CI, triage artifacts are uploaded (.soothfast/triage/)
and a summary is posted as a PR comment. A genuine regression should be fixed;
an intentional cost change should be justified in the PR description so the
maintainer can re-baseline the affected assertions.
- Run
cargo fmt --allbefore committing. CI enforces formatting. - Run
cargo clippy --workspace --all-targets -- -D warnings. All warnings are errors in CI. - Public API items must have doc comments (
///). See existing items for tone and style. - No
unwrap()orexpect()in library code outside of tests. - Prefer small, focused commits. One logical change per commit.
Dependencies are a measured, gated claim in this project.
The full CLI dependency tree is intentionally tiny (~12–15 crates), and the
core soothfast library that user crates depend on stays at essentially
linkme + proc-macro machinery. Hand-rolled beats imported here: stats,
markdown scanning, SVG charts, and git operations are all written in-repo.
A PR that adds a dependency must justify it in the description and will be
held to the cargo deny policy (deny.toml): crates.io only, no wildcard
versions, allowed licenses only.
- Fork the repository and create a branch from
master:git checkout -b feat/my-feature
- Make your changes, including tests for any new behaviour.
- Run
make checkandmake gatelocally. Fix any issues. - Push your branch and open a PR against
master. - Fill in the PR description: what changed, why, and how you tested it.
- Address any review comments. All CI checks — including the dogfood gate — must pass before merge.
PRs that add features or fix bugs should include tests. PRs that only touch documentation or CI do not require new tests.
<type>: <short description>
<optional body — explain the why, not the what>
Types: feat, fix, refactor, docs, test, chore, perf, ci,
deps
Examples:
feat: add async backend poll/wake counters
fix: handle empty rustdoc JSON surface in docs diff
perf: avoid re-parsing callgrind output per assertion
Releases are managed by the project maintainer. Publishing to crates.io is
automated: pushing a v* tag runs the full check suite and the gate, then
publishes the workspace crates in dependency order (see
.github/workflows/release.yml). If you believe a bug fix warrants a release,
comment on the relevant issue.
To cut a release:
- Land a PR that renames
CHANGELOG.md's top## Unreleased (...)heading to## X.Y.Z - YYYY-MM-DD.changelog.ymlonly ever regenerates a heading that starts withUnreleased; anything else is treated as a released section and left alone, so this rename is what turns the draft into that release's permanent changelog entry. - Bump
versionin the workspace[workspace.package]table (Cargo.toml) to match. On a minor or major bump, raise the intra-workspace requirements in[workspace.dependencies]too: they are caret ranges, so a0.1.xentry excludes0.2.0and the workspace stops resolving. - Read the section you are about to freeze and check it lists what the
release actually contains.
changelog.ymlregenerates against the tree its run checked out, so a regeneration that started before the last merge lands a section that is correct for a tree that no longer exists. Verify the artifact, never the process that produced it. If you cut more than once, diff the new section against the previous cut: a correction that only ever landed in the frozen section is reverted silently by the next cut, and an absence is what reading misses. - Tag the merge commit
vX.Y.Zand push the tag.release.ymlverifies the tag matches the workspace version, runs checks, extracts the matching## X.Y.Zsection for the GitHub Release notes, and publishes. - The
publishjob waits on thereleaseenvironment's required reviewers before it touches crates.io — approve the deployment when you're ready.