git clone https://github.com/akitaonrails/ai-memory
cd ai-memory
cargo build --workspace
cargo test --workspace --all-targetsRust 1.95 is required (pinned in rust-toolchain.toml). The build is
self-contained: SQLite is bundled via rusqlite's bundled feature, and
libgit2 is vendored via git2's vendored-libgit2 feature. No system
libraries need installing beyond a standard C toolchain.
GitHub associates commits with accounts through the author email stored in each commit. Before pushing a branch, inspect every commit that the pull request will add:
git log --format='%h %an <%ae>' "$(git merge-base HEAD origin/main)"..HEADUse an email verified by your GitHub account, or its GitHub-provided noreply
address. Set it for this checkout when your global Git identity belongs to a
different project or employer:
git config --local user.name "Your Name"
git config --local user.email "your-verified-address@example.com"Correct attribution mistakes on the pull-request branch before it is merged.
The project does not rewrite shared main history or published release tags
solely to change attribution because doing so invalidates commit hashes and
breaks existing clones and forks. Maintainers use .mailmap to
canonicalize accidental aliases without changing published commits.
All of these must pass; CI enforces them and so does bin/release. The build
is self-contained, so none of them needs an environment variable.
cargo fmt --all -- --check
git diff --check
cargo clippy --workspace --all-targets -- -D warnings
cargo tf # every test (alias: cargo nextest run -P full)
cargo deny check # dependency policycargo tf needs nextest (cargo install cargo-nextest --locked); without it,
cargo test --workspace --all-targets is the equivalent. Linux and macOS CI run the
same tests with cargo nextest run --workspace --all-targets --profile ci.
If cargo-deny or cargo-audit are not installed:
cargo install cargo-deny cargo-auditcargo t # all but the slow tier, ~20s warm
cargo t -p ai-memory-store # one crate: builds only its test binaries
cargo t -E 'test(/purge/)' # one topic (builds everything, runs a subset)The everyday profile skips tests by name: any module segment starting with
slow or stress (packaging::slow::* drives the real wrapper scripts and
fake container engines at 10-20s each; stress_* modules hammer concurrency).
The budget for everything else is about 1s per test alone, and the profile
lists anything over 5s in its summary. Fix a slow test before tiering it.
Skipped tests still count as "skipped" in the summary, never hidden, and two
independent things run them anyway: the pre-push hook and CI.
Install the hook once per clone with scripts/install-git-hooks.sh (from Git
Bash on Windows). It can run from the main checkout or a linked worktree;
both use the shared repository hook directory. Reinstallation replaces
ai-memory's managed block in place, preserving surrounding user commands and
their order. If core.hooksPath is set, the installer stops before writing;
integrate the block through your existing hook manager instead. Incomplete or
duplicate managed markers also stop installation and leave the hook unchanged.
Bypass it on a work-in-progress branch with git push --no-verify.
The managed test block clears Git's repository environment and disables global
and system Git configuration for Cargo and its children. Fixture commands can
then use their own repositories without inheriting the checkout being pushed.
The publishing Git process and other hook code retain their configuration, and
the block's shell options stay inside it; a failing test run still fails the
hook even when your own commands follow the block without set -e.
Run the installer again to update an existing installation.
Companions have separate Cargo workspaces. Check each changed companion with
cargo fmt, cargo clippy --all-targets -- -D warnings, and cargo test, passing
its --manifest-path. Changes to the lifecycle relay also need the real-server
test documented in its README.
Integration tests live in tests/suite/ per crate and compile into the
crate's own test harness (declare a new file with mod name; in
tests/suite/mod.rs); only the CLI keeps a separate test binary, because its
tests run the built executable. Helpers shared across crates go in
crates/ai-memory-test-support. Platform-specific speedups
(macOS Keychain, Windows linker and Defender) are in AGENTS.md.
Every user-facing change must add a CHANGELOG.md entry under
## [Unreleased] in the same PR. User-facing means: a new CLI flag or
subcommand, env var, HTTP/admin endpoint, MCP tool or tool-response field,
.ai-memory.toml marker key, any changed behaviour or default, or an
observable bug fix. Internal refactors, dead-code removal, and test-only
churn are exempt.
This has been the most often forgotten obligation across review batches,
so reviewers treat a missing entry as blocking, and the PR template has a
checkbox for it. Follow the existing entry style (past-tense summary,
trailing ([#NNN]) PR/issue reference) and place it under the right
### Added / ### Changed / ### Fixed heading.
CHANGELOG.md holds the current major line only. Older lines are archived per
major version in docs/changelog/; bin/release moves the
previous major there when it cuts an X.0.0. Released sections are frozen in
both places, and CI rejects an edit to any of them.
The full rules are in AGENTS.md, the single
canonical agent/contributor rules file (CLAUDE.md is just a pointer to
it). Short version:
- Keep changes small and scoped to the task; avoid adjacent feature work.
- No dead code or half-built features. Document future work in
docs/design notes rather than shipping unreachable stubs. - Write tests before claiming done. Parsers, ID derivation, and retention/decay math especially.
- Avoid opportunistic refactors or speculative abstractions.
- Comments explain why, never what. No comments that restate the line above them.
- The core stays Rust-only. The server, its built-in web UI and everything
it ships build with Cargo alone: no Node, npm, bundler or JavaScript
framework. The web UI is server-rendered from askama templates, with at
most a small inline vanilla script for progressive enhancement. A UI that
needs a JavaScript toolchain, or a client library for another ecosystem
(the TypeScript SDK, the Swift macOS app), belongs under
companions/.
Never violate any of the invariants in AGENTS.md (see the
"Cross-cutting invariants" and "Project maintenance rules" sections).
Highlights for contributors:
- All SQLite writes go through the single writer actor (
WriterHandle). - ai-memory settings are read once by
Config::load()and passed down. Process and third-party harness environment reads use injectable lookups. - Atomic file writes only: tmp + rename + fsync; never write in-place.
- Every wiki page is identified by
(workspace_id, project_id, path). - Keep server-facing CLI commands thin HTTP clients. Offline lifecycle
operations (
reset,restore,reindex, anduninstall --purge-data) access disk directly and must retain their live-process guards.backupuses the running server's online SQLite backup API.
Human CLI and web messages use embedded Fluent catalogs. See
interface languages for message ownership, plural and
escaping rules, adding languages, and the catalog checks. Update every shipped
language when changing an existing message or CLI help description. A new CLI
bail!, anyhow!, .context or diagnostic message also needs an entry in
each diagnostics.ftl; every_cli_diagnostic_has_a_catalog_entry lists any
that are missing.
This project follows Semantic Versioning:
- Patch (
x.y.Z): bug fixes that do not change public API or on-disk format. - Minor (
x.Y.0): additive changes; new CLI subcommands, new MCP tools, new config keys. Existing behaviour is preserved. - Major (
X.0.0): breaking changes. This includes on-disk format changes that are not handled by a migration, removal of CLI subcommands, or changes to the MCP tool schema that would break existing agents.
Breaking changes only ship in major releases. Deprecated items are
documented in the CHANGELOG under ### Deprecated and removed no sooner
than the following major release.
- Put your CHANGELOG entry under the heading that matches its semver
impact:
### Fixedfor bug fixes,### Addedfor new capabilities,### Changedfor altered behaviour. The maintainer reads the[Unreleased]section to pick the next version number, so a fix filed underAdded(or vice versa) can bump the wrong release. - If your change is breaking (on-disk format, removed/renamed
surface, changed MCP schema), say so explicitly in the PR description
so it gets the
breaking-changelabel and is scheduled for the next major instead of blocking patch/minor releases. - Bug fixes ship in the next patch release, usually promptly, and are not held for feature releases. Small additive features (a new agent harness, LLM provider, install client) ship in the next minor.
- Pick the base branch from that impact. A fix targets
main. A new capability (flag, subcommand, MCP tool, config key, agent harness, provider, companion) targets the standing feature branch, currentlyrelease/3.5. A fix to code that exists only on the feature branch targets that branch too. - Check what already landed first. Before opening a PR, look for the
issue's closing commit and search
git log origin/main origin/release/3.5for the feature or fix. A closed issue usually means the work shipped; a PR that redoes it gets closed, however good it is.