Skip to content

Development

Steven Enamakel edited this page Sep 1, 2026 · 2 revisions

Development

Run everything from the repository root.

The contract

CI runs exactly these four, so a green local run should mean a green CI run.

cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo build --all-targets --all-features
cargo test --all-features

Plus the guards:

.github/scripts/assert-pure.sh
.github/scripts/check-file-coverage.sh 90 coverage.json

assert-pure.sh asserts that the pure crates took on no async runtime, transport, HTTP client, web framework, SQL client, git implementation, or anyhow. The enumerated list in that script is the source of truth, and it is not advisory: adding an exception to it in order to land a change is not an option.

Every source file must hold at least 90% line coverage.

Useful subsets

cargo fmt --all                                   # format before committing
cargo test -p tinyhivemind-core                   # one crate's suite
cargo test --doc                                  # doctests alone
cargo doc --no-deps --all-features                # what CI builds with -D warnings
cargo run -p tinyhivemind-core --example basic
cargo run -p tinyhivemind-hive --example hive
cargo run --release -p tinyhivemind-hive --example bench -- --trace

Structure

One directory per crate under crates/, one directory per feature area under each crate's src/. A module root explains the module, wires its pieces, and exposes the smallest useful API. Substantial type definitions go in types.rs, module-local unit tests in test.rs, wired from the bottom of the module root:

#[cfg(test)]
mod test;

No inline mod tests blocks in implementation files. No utils.rs or helpers.rs, which are a symptom of a module nobody named yet. Many small modules that each do one thing beat a few broad ones.

Adding a crate means creating crates/<name>/. members = ["crates/*"] picks it up by existing.

Testing

Unit tests may touch private items. Integration tests in crates/<crate>/tests/ exercise only the public API, and they are the regression suite for the crate's contract.

Payload types pin their serde representation in a unit test, because that representation is the wire form. A host and a module that disagree about a field name fail at runtime with a decode error.

Tests are deterministic and independent of network, wall clock, and execution order. Anything live is gated behind a feature or an env var and named live_* so it is easy to exclude.

Write the test first when fixing a bug: a failing test that reproduces the report, then the fix that turns it green. Never skip, ignore, or delete a failing test to make a command pass.

Errors

One crate-wide Error enum per crate in src/error/mod.rs, built with thiserror. Fallible public functions return the crate's Result<T>. Add a specific variant rather than stuffing context into a string. Messages are lowercase without trailing punctuation.

No unwrap(), expect(), or panic! in library code paths. They are fine in tests, examples, and genuinely unreachable states, where an expect carries a message explaining the invariant.

Every public fallible function documents a # Errors section, and anything that can panic documents # Panics.

Dependencies

Adding one is a design decision. Check whether the standard library or an existing dependency already covers it. When you do add one: pin a caret range, enable only the features you need, gate anything optional behind a documented Cargo feature, declare it once in the root [workspace.dependencies] if more than one crate needs it, and leave a comment above the entry explaining why the crate is there and what uses it.

There are no vendored dependencies, and that is deliberate. This repository is itself vendored, so anything it vendored in turn would become a nested submodule in every consumer.

Contributing

Branch first, one branch per logical change, and do the work in a git worktree so the main checkout stays clean. Commit subjects are concise and imperative. Keep commits small enough that each one builds on its own, and avoid mixing formatting, refactors, and behaviour changes unless they are genuinely inseparable.

Open pull requests ready for review rather than as drafts. A pull request summarizes what changed and why, calls out public API or behaviour changes explicitly or states "None", lists the validation commands actually run with their outcomes, links the related issue, and includes updated tests, docs, and examples in the same change.

Address review feedback by fixing it, and reply on each thread describing what changed. Do not resolve a thread whose feedback you have not addressed or explicitly declined with a reason.

AGENTS.md in the repository root is the full working agreement for humans and coding agents alike. CLAUDE.md is a symlink to it, so every agent reads the same file.

Clone this wiki locally