Contributing to mxr
May 21, 2026 ยท View on GitHub
Dev setup
git clone https://github.com/planetaryescape/mxr
cd mxr
cargo build --workspace
cargo test --workspace
If you need the docs site too:
cd site
npm install
npm run build
Non-negotiables
- local-first first
- SQLite is canonical state
- search index is rebuildable
- daemon is the system
- TUI and CLI are both clients
- web is also a client of daemon IPC
- provider-specific logic stays in adapter crates
- compose uses
$EDITOR - rules are deterministic before they are clever
- plain-text-first rendering wins over flashy rendering
- semantic search is an
mxr-platformfeature, not a core mail primitive - semantic embeddings stay local
- semantic indexing uses real text extraction only; do not casually reintroduce OCR
- lexical exactness and semantic recall are different layers; do not blur them
IPC boundaries
Classify every IPC addition before you add it:
core-mailmxr-platformadmin-maintenanceclient-specific
Rules:
- Core mail/runtime should stay boring and stable.
- mxr platform capabilities are first-class. Do not bury them as misc.
- Admin surfaces stay in IPC, but separate them mentally and in code from the mail contract.
- Client-specific shaping stays in clients.
- The daemon serves reusable truth/workflows, not screen payloads.
Crate boundaries
Keep these intact:
mxr-coredepends on nothing internal.mxr-protocoldepends only onmxr-core.- Provider crates depend on
mxr-coreplus shared mail utility crates only (mail-parse,outbound). mxr-storeandmxr-searchdepend only onmxr-core.mxr-syncdepends oncore + store + search.mxr(daemon crate) is the integration point.mxr-tuiandmxr-webare client crates. They may use local utility crates (config,compose,reader,mail-parse), but never daemon/store/search/sync/provider crates.- Do not use
#[path]includes to simulate crate boundaries.
Semantic boundary:
mxr-semanticis the local dense-retrieval/runtime layer- sync may persist semantic chunk data even when semantic retrieval is disabled
- embeddings are generated only when semantic is enabled or explicitly reindexed
- field-aware hybrid search semantics are intentional
- OCR is out of scope for active semantic indexing
Lifecycle guardrails:
- sync itself guarantees SQLite persistence + lexical freshness
- daemon post-sync work ingests semantic chunks for changed messages
- lexical startup repair is mandatory and rebuilds from SQLite
- semantic readiness is optional derived state; do not make core mail depend on it
Repo reality:
- The product/install/package surface is the repo-root package
mxr. - Internal crates under
crates/are real workspace crates and are private by default (publish = false). - The IMAP adapter depends on the published
mxr-async-imapfork from crates.io; vendored source is not part of the workspace boundary model. - Provider paths are
provider-gmail,provider-imap,provider-smtp, andprovider-fake. crates/webis a current client/bridge surface.
Required checks
Run all of these before sending changes:
cargo fmt --all -- --check
cargo nextest run --workspace
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
cargo sqlx prepare --check --workspace
cargo deny check
Before claiming a PR is Rust-clean, update the floating stable toolchain and run the local CI mirror:
rustup update stable
scripts/pre-pr-rust-gate
The script prints the active rustc and Clippy versions, then runs the Rust
hygiene checks that most often drift from local machines to GitHub CI:
cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
scripts/check_architecture_boundaries.sh
cargo deny check
For test-quality and web-facing changes, also run:
scripts/pre-pr-test-gate
That gate validates the test-quality audit script, runs strict test-quality audit, and runs the web typecheck/lint/unit test suite.
Test helpers may use narrow #[expect(clippy::...)] annotations when
panic/unwrap/default-mutation keeps fixture failures clearer. Keep the
annotation on the smallest test module or function that needs it, and include
the reason.
If you touch the docs site:
cd site
npm install
npm run build
Real-system verification
Green unit tests are not enough. For user-facing work, also test the real flow:
cargo run -- daemon --foreground
mxr status
mxr search "label:inbox"
mxr doctor --check
If you changed rules, exports, labels, notify, events, or logs, exercise the matching CLI surface too.
If local dev state gets messy, prefer the built-in reset flow over manual file hunting:
mxr reset --hard --dry-run
mxr burn --dry-run
That wipes rebuildable local runtime state only. It does not delete config.toml or system credentials by default.
Running the daemon
mxr daemon --foreground is the canonical manual-test entrypoint. Keep it running in one terminal, then use a second terminal for CLI smoke tests like mxr status, mxr sync --status, mxr search, and the mutation flow you changed.
PR process
- Fork the repo.
- Create a focused branch from
main. - Keep the diff surgical.
- Run the required checks.
- Open a PR with enough context to reproduce and verify.
CI must pass before review or merge.
Rules for changes
- Keep blast radius small.
- Do not refactor adjacent code unless the task requires it.
- Add tests for new behavior.
- Prefer integration coverage over mock-heavy unit tests.
- Do not wire a daemon feature for only one client surface when both TUI and CLI need it.
- If you touch semantic search, update both code and docs. The behavior is subtle enough that stale docs become bugs.
Adapter work
Adapter crates are replaceable by design.
When adding or changing an adapter:
- Keep provider-specific code inside the adapter crate.
- Map into the mxr internal model, not the other way around.
- Validate against fake/conformance coverage.
- Document any provider semantic mismatch honestly.
Docs and release hygiene
- Update
README.mdfor changed user-facing behavior. - Update
site/docs for new commands or workflows. - Update architecture/blueprint docs when code changes invalidate older assumptions.
- Keep
.github/workflows/aligned with the actual build and release process. - Keep issue templates and bug-report flow current.
Architecture pointer
Start with ARCHITECTURE.md, then use the blueprint and implementation journey for phased history and maintainer context.
Good first issues
Look for the good first issue label if you want a bounded starting point.
Useful references
- ARCHITECTURE.md
docs/blueprint/docs/implementation-journey.mddocs/blueprint/15-decision-log.mddocs/blueprint/17-release-pipeline.mddocs/blueprint/18-bug-reporting.md