Welcome! We appreciate your interest in contributing to ICM.
ICM (Infinite Context Memory) is a persistent long-term memory for LLM agents, written in Rust. It stores memories with embeddings in SQLite, does hybrid retrieval (BM25/FTS5 + vector similarity), manages temporal decay and consolidation, and exposes an MCP server so tools like Claude Code, Codex, and others can store and recall memories across sessions.
| Type | Examples |
|---|---|
| Report | File a clear issue with steps to reproduce, expected vs actual behavior |
| Fix | Bug fixes, correctness issues, durability/robustness improvements |
| Build | New features (for core features — storage backends, retrieval, MCP tools — discuss with maintainers first) |
| Review | Review open PRs, test changes locally, leave constructive feedback |
| Document | Improve docs, clarify behavior |
A few principles guide ICM. Understanding them helps your contribution fit naturally.
ICM is positioned as a durable, cross-host brain shared by multiple agents writing the same store concurrently. Data loss and silent corruption are the worst failure modes. Prefer the safe, boring path; back up before destructive operations; never claim a fix you can't verify.
No .unwrap() / .expect() / panic! in non-test code. Use typed errors (thiserror) in libraries and anyhow with .context() at the CLI boundary, and propagate with ?. A hook or MCP tool must degrade gracefully, never crash the caller.
All I/O is async where it matters. Keep the per-hook and per-prompt paths cheap — they run on every tool call. Don't load heavy models or do network I/O on a path that must return in milliseconds.
SQLite is the default in-process backend; Postgres/OpenSearch are compiled in and chosen at runtime via ICM_DB_BACKEND. New storage features should respect this split and not assume SQLite.
Reuse existing components and traits (MemoryStore, Embedder, …) instead of duplicating. New core features (backends, retrievers, MCP tools) are worth discussing before you build.
ICM uses Conventional Commits and release-please to auto-generate CHANGELOG.md, version bumps, and GitHub releases. Never edit CHANGELOG.md manually — it is fully managed by release-please from your commit messages.
<type>(<scope>): <short description>
| Type | Semver Impact | When to Use |
|---|---|---|
feat |
Minor | New features, new MCP tools, new backends |
fix |
Patch | Bug fixes, corrections |
perf |
Patch | Performance improvements |
refactor |
— | Code restructuring (no changelog entry) |
docs |
— | Documentation only |
chore |
— | Maintenance, CI, deps |
feat! / fix! |
Major | Breaking changes (add ! after type) |
Scope should match the module or area: store, mcp, retriever, hooks, cli, cicd, etc.
feat(mcp): add icm_memory_health tool
fix(store): open read-only connections WAL-aware instead of immutable
perf(retriever): reuse the FTS statement across recall calls
feat!(store): change the embedding column layout
These commit messages become CHANGELOG entries when release-please cuts a release. Write them as if users will read them.
Git branch names cannot include spaces or colons, so we use slash-prefixed names.
| Prefix | When to Use |
|---|---|
fix/ |
Bug fixes, corrections, minor adjustments |
feat/ |
New features |
chore/ |
CI/CD, deps, maintenance, breaking changes |
Combine the prefix with a scope if it adds clarity and finish with a short, kebab-case slug:
fix/store-readonly-live-connection
feat/mcp-http-proxy-mode
chore/release-pipeline-cleanup
Each PR must focus on a single feature, fix, or change. The diff must stay in-scope with the PR title and body. Out-of-scope changes (unrelated refactors, drive-by fixes, formatting of untouched files) go in a separate PR. For large features, prefer several logical, independently-reviewable PRs over one enormous one.
git checkout develop
git pull origin develop
git checkout -b feat/scope-your-clear-descriptionRespect the existing workspace layout (crates/icm-*). Keep functions short and focused. Comments explain why, not what.
Every change must include tests where it has a runtime surface. See Testing.
Update docs for new features and changes to already-documented behavior.
Open your Pull Request against the develop branch. main is reserved for stable releases — only maintainer develop → main PRs (cut via release-please) target it.
- Maintainer review — a maintainer reviews for quality and alignment.
- CI/CD checks — automated tests and lints must pass.
- Resolution — address feedback from review or CI.
your branch --> develop (review + CI + integration) --> main (versioned release via release-please)
All three must pass before any PR:
cargo fmt --all --check && cargo clippy --workspace --all-targets -- -D warnings && cargo test --workspace- Unit tests added/updated for changed code
- Integration/e2e coverage where the change has a runtime surface
- No
.unwrap()/.expect()/panic!in non-test code -
cargo fmt --all --check && cargo clippy --workspace --all-targets -- -D warnings && cargo test --workspacepasses - Manual test: exercise the affected flow (CLI command, MCP tool, hook) and inspect the result
- Bug reports & features: Issues
- Discussions: GitHub Discussions
For external contributors: your PR undergoes automated and manual security review (see SECURITY.md).
Thank you for contributing to ICM!