AI agents: read .claude/skills/ajj-guide.md for a comprehensive crate
overview before making changes.
ajj is a JSON-RPC 2.0 router library. Single crate (not a workspace). Core
pipeline: Handler -> Method -> Router -> Server. Feature-gated for axum, ws,
and ipc transports. Built on tower and serde_json.
cargo +nightly fmtcargo clippy -p ajj --all-features --all-targetscargo clippy -p ajj --no-default-features --all-targetscargo t -p ajj
Always lint with both --all-features and --no-default-features before
committing. Never use cargo check or cargo build.
A Claude hook in .claude/settings.json runs .claude/hooks/pre-push.sh
before every git push. The push is blocked if any check fails. The checks:
cargo +nightly fmt -- --checkcargo clippy -p ajj --all-targets --all-features -- -D warningscargo clippy -p ajj --all-targets --no-default-features -- -D warningsRUSTDOCFLAGS="-D warnings" cargo doc -p ajj --no-deps
Clippy and doc warnings are hard failures.
- Feature gates:
axum,pubsub,ws,ipc. Default:axum,ws,ipc.wsandipcboth implypubsub. Router<S>wrapsArc<RouterInner<S>>.Sis "missing" state, not current state. Afterwith_state(),Sbecomes the next missing type.- Handlers are type-erased via
ErasedIntoRouteand stored inBTreeMap<MethodId, Method<S>>. - Internal macros
tap_inner!,map_inner!,panic_on_err!,impl_handler_call!are used extensively in router and handler code. - OpenTelemetry spans follow OTEL semantic conventions for JSON-RPC.
- Code provenance: some code adapted from axum and alloy. Files containing adapted code have license notices at the bottom.
- Prefer building crate docs (
cargo doc) and reading them over grepping.
- Functional combinators over imperative control flow. No unnecessary nesting.
- Terse Option/Result handling:
option.map(Thing::do_something)orlet Some(a) = option else { return; };. - Small, focused functions and types.
- Never add incomplete code. No
TODOs for core logic. - Never use glob imports. Group imports from the same crate. No blank lines between imports.
- Visibility: private by default,
pub(crate)for internal,pubfor API. Never usepub(super).
thiserrorfor library errors. Neveranyhow. Nevereyrein libraries.- Propagate with
?andmap_err.
- Use
tracingcrate. Instrument work items, not long-lived tasks. skip(self)when instrumenting methods. Add only needed fields.- Levels: TRACE (rare, verbose), DEBUG (sparingly), INFO (default), WARN (potential issues), ERROR (prevents operation).
- Propagate spans through task boundaries with
Instrument.
- Tokio runtime conventions. No blocking in async functions.
- Long-lived tasks: return a spawnable future, don't run directly.
- Short-lived spawned tasks: consider span propagation.
- Tests panic, never return
Result. Useunwrap()directly. - Unit tests in
mod testsat file bottom. Integration tests intests/.
- Doc all public items. Include usage examples in rustdoc.
- Hide scaffolding with
#. Keep examples concise. - Traits must include an implementation guide.
- Fresh branches off
mainfor PRs. Descriptive branch names. - AI-authored GitHub comments must include
**[Claude Code]**header.