feat: add a persistent lifecycle relay companion (#822) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm # Conflicts: # .github/workflows/ci.yml # CHANGELOG.md # crates/ai-memory-cli/tests/suite/packaging.rs # scripts/install-git-hooks.sh
8.6 KiB
Contributing to ai-memory
Dev setup
git clone https://github.com/akitaonrails/ai-memory
cd ai-memory
cargo build --workspace
cargo test --workspace --all-targets
Rust 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.
Commit attribution
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)"..HEAD
Use 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.
Required gates before push/merge
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 policy
cargo tf needs nextest (cargo install cargo-nextest --locked); without it,
cargo test --workspace --all-targets is the equivalent and is what CI runs.
If cargo-deny or cargo-audit are not installed:
cargo install cargo-deny cargo-audit
The everyday loop
cargo 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.
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. 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.
CHANGELOG is a merge gate
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 single most-forgotten obligation across review batches,
so reviewers treat a missing entry as blocking — 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.
Workflow rules (condensed from AGENTS.md)
The full authoritative rules are in AGENTS.md — the single
canonical agent/contributor rules file (CLAUDE.md is just a pointer to
it). Short version:
- Work milestone by milestone. Do not start M(n+1) until every "Done when"
bullet in M(n) passes (see
docs/design-decisions.md). - No dead code, no half-built features. Stubs are documented with
// M<n> TODOin the module doc-comment. - Write tests before claiming done. Parsers, ID derivation, and retention/decay math especially.
- Do not refactor outside the milestone. Only touch what the current milestone requires.
- Comments explain why, never what. No comments that restate the line above them.
Cross-cutting invariants
Never violate any of the invariants in AGENTS.md (see the
"Rust Engineering Rules" and "Project Maintenance Rules" sections).
Highlights for contributors:
- All SQLite writes go through the single writer actor (
WriterHandle). - Config is read once at startup; never call
std::env::varoutsideConfig::load. - Atomic file writes only: tmp + rename + fsync; never write in-place.
- Every wiki page is namespaced by
(workspace_id, project_id). - The CLI is always a thin HTTP client to the running server — it never opens the SQLite file or the wiki directory directly.
Versioning and deprecation policy
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.
How this affects your PR
- 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 — they are not held for feature releases. Small additive features (a new agent harness, LLM provider, install client) ship in the next minor.