Cross-platform load tester for MCP (Model Context Protocol) servers. Detects deadlocks, hangs, and perf regressions that unit tests miss.
These rules govern how Claude works on this repo. They override Claude defaults.
- Never
git push(any remote, any branch) unless the user confirms it in the current message. - Never make the repo public. It stays private until the user explicitly says "ok to public".
- Never
git tag, create releases, or publish to crates.io without confirmation. - Never
git push --force— also denied by.claude/settings.json. - Commits land locally without confirmation. Just announce big or risky commits before making them.
- Do not add
Co-Authored-By: Claudetrailers in commit messages.
- Briefly state the plan before destructive or multi-step actions (max ~5 lines).
- Skip the explanation for trivial actions (single file read, lint check, one-line edit).
- When intent is ambiguous, ask before guessing.
- Slash commands chain through phases (e.g.
/simplify→/security-review→/code-review→/engineering:documentation). - For large multi-file work, split across parallel agents with disjoint file ownership — two agents must never edit the same file.
- After all agents return, the orchestrator (main Claude session) runs
cargo build+cargo clippy -D warnings+cargo test+cargo fmtto integrate. - Commit in logical groups (
security:/fix:/test:/docs:) — not one large blob.
- All testing, linting, and CI smoke runs locally on this machine only. Never trigger remote CI before push.
- Test:
cargo nextest run --workspace --all-features(fallback:cargo test). - Lint:
cargo fmt --check && cargo clippy --workspace --all-targets -- -D warnings. - CI smoke (full):
bash scripts/ci-checks.sh— Windows:pwsh scripts/ci-checks.ps1. - Build: prefer
cargo build --workspace --all-targetsto catch test-only breakage early.
- Any
git push(any remote, any branch). - Making the repo public, transferring ownership, or changing visibility.
git tag/ publishing to crates.io / creating a GitHub Release.- Invasive refactors: splitting files over the 300-line convention, introducing cargo feature flags, renaming public API.
cargo update(and the lockfile commit it produces — must be its own commit).- Adding new top-level
*.mdfiles at the repo root. - Deleting any committed file or directory.
- Code →
src/of the relevant crate. Files < 300 lines (split when longer). - Unit tests →
#[cfg(test)] mod tests { }next to the code under test. - Integration tests →
crates/<crate>/tests/<name>.rs. Spawn fixtures viahelpers::spawn_fixture, notstd::process::Command. - Docs → don't add new top-level
*.mdwithout confirmation. Sub-docs underdocs/are fine. - CHANGELOG →
[Unreleased]must be updated for every user-visible change (new flag, behavior change, public API delta, deprecation). - ADRs →
docs/adr/NNNN-title.md(next number) for any architectural decision; updatedocs/adr/README.md.
- Test:
cargo nextest run --workspace --all-features(fallback:cargo test --workspace --all-features) - Lint:
cargo fmt --check && cargo clippy --workspace --all-targets -- -D warnings - CI check (full):
bash scripts/ci-checks.sh— Windows:pwsh scripts/ci-checks.ps1 - Bench:
cargo bench -p mcp-loadtest - Run tool against a real MCP server:
cargo run -p mcp-loadtest-cli -- deadlock-probe -s "python -m foo"
- Design: DESIGN.md — what we're building (20 sections, types/algorithms/test-matrix)
- Project structure: PROJECT-STRUCTURE.md — how this repo is laid out
- Decisions: docs/adr/ — append-only architecture decisions
- Core library:
crates/mcp-loadtest/src/ - CLI:
crates/mcp-loadtest-cli/src/ - Mock fixtures:
crates/mcp-loadtest/tests/fixtures/*.py
- Files < 300 lines of production code (in-file
#[cfg(test)] mod testsblocks are excluded from the count — they stay co-located with the code they test per Rust idiom). Split production code if longer. - Errors:
thiserrorenums, never bareunwrap()in lib code (allowed in tests). - No
panic!()outside tests. UseResult<T, RunError>. - Public API stable across
0.xper CHANGELOG. Breaking changes: bump minor, document. - No
tokio::spawn(async move { ... })without keeping theJoinHandleand observing cancellation. - Async paths must not call blocking I/O — use
tokio::fs,tokio::process. - Tests prefer
helpers::spawn_fixture(...)over rawstd::process::Command.
| Want to add... | Where / how |
|---|---|
| New scenario | crates/mcp-loadtest/src/scenario/CLAUDE.md — or /new-scenario <name> |
| New mock fixture | crates/mcp-loadtest/tests/fixtures/CLAUDE.md — or /new-mock <name> |
| New ADR | docs/adr/NNNN-title.md (next number); update docs/adr/README.md |
| New CLI flag | crates/mcp-loadtest-cli/src/main.rs clap struct; thread through to lib via Run builder |
- Don't run
cargo updatewithout committing the lock change in a separate commit. - Don't add
#[allow(clippy::...)]without an inline// reason: ...comment. - Don't change public types in
lib.rswithout bumping CHANGELOG[Unreleased]. - Don't introduce blocking I/O in async paths.
- Don't push to
maindirectly. PRs only. - Don't
git push --force(denied by.claude/settings.json).
Run /release-checks before tagging. See .claude/commands/release-checks.md for what it does.
- v0.1.0 — tagged (
v0.1.0→a3ff6d4, annotated, pushed); CI green (368 tests); M1–M7 + post-M7 shipped - Distribution:
cargo install --git+ GitHub Release binaries; crates.io deferred (ADR 0015, amends ADR 0004) - Pending gate before release: repo → public, then Gate A–D in docs/RELEASE.md