Thank you for your interest in contributing! This guide covers environment setup, build and test commands, coding conventions, and the PR process.
| Tool | Version | Purpose |
|---|---|---|
| .NET SDK | version pinned in global.json |
Build and test |
| Docker Desktop | Any recent | Testcontainers (integration tests start Neo4j 5.x automatically — no manual Neo4j install needed) |
| Git | Any | Source control |
git clone https://github.com/joslat/agent-memory-dotnet.git
cd agent-memory-dotnet
dotnet restore AgentMemory.slnxdotnet build AgentMemory.slnxThe solution is configured via Directory.Build.props:
- Target frameworks: publishable
src/libraries multi-target net8.0, net9.0, and net10.0; tests, samples, and tools build against whatever single framework their own.csprojspecifies - Nullable reference types: enabled
TreatWarningsAsErrors: true for allsrc/projects (disabled fortests/)
A clean build must produce zero errors and zero warnings in src/. PRs that introduce warnings will not be merged.
dotnet test tests/AgentMemory.Tests.Unit/AgentMemory.Tests.Unit.csprojUnit tests cover all packages and use stub implementations (no Neo4j, no LLM required).
dotnet test tests/AgentMemory.Tests.Unit.SemanticKernel/AgentMemory.Tests.Unit.SemanticKernel.csprojdotnet test tests/AgentMemory.Tests.Integration/AgentMemory.Tests.Integration.csprojIntegration tests use Testcontainers to spin up a disposable Neo4j 5 container automatically. Docker Desktop must be running.
dotnet test AgentMemory.slnxThe solution follows a strict ports-and-adapters (hexagonal) architecture:
Abstractions ← Core ← Neo4j / Extraction / AgentFramework / SemanticKernel / ...
Abstractionsdefines all domain types and service interfaces. It has zero external dependencies (onlyMicrosoft.Extensions.AI.Abstractions).Coreimplements services and the extraction pipeline. It depends only onAbstractions.Neo4jprovides Neo4j repository implementations. It depends onAbstractions+Core.- Adapter packages (
AgentFramework,SemanticKernel,McpServer, etc.) depend inward onAbstractionsand optionallyCore. They never reference each other orNeo4j.
Do not add framework dependencies (MAF, SK, Neo4j Driver) to Abstractions or Core. Violations will be caught in review.
All Cypher query strings are stored as typed constants in domain-specific Queries/ classes within AgentMemory.Neo4j. Do not inline Cypher strings in repository implementations.
AgentMemory.Neo4j/
└── Queries/
├── MessageQueries.cs
├── EntityQueries.cs
├── FactQueries.cs
└── ... (one file per domain)
All domain types are sealed record types with required properties for spec-mandated fields. Timestamps use DateTimeOffset with a Utc suffix (e.g., CreatedAtUtc). Collections use IReadOnlyList<T> / IReadOnlyDictionary<K,V> and default to empty (never null).
All async methods accept a CancellationToken cancellationToken = default parameter. Pass the token through every layer.
Stub implementations live in AgentMemory.Core/Stubs/ and are used in unit tests. They implement the same interfaces as production types and return deterministic results without any external service dependency.
The codebase maintains zero TODO, FIXME, or HACK comments. Complete your work before raising a PR or track follow-up work via GitHub issues.
feature/<short-description>
fix/<short-description>
docs/<short-description>
refactor/<short-description>
<type>: <short imperative summary>
<optional body: why this change, not what>
Types: feat, fix, docs, refactor, test, chore.
Example:
feat: add RecallAsOfAsync temporal point-in-time recall
Allows agents to reconstruct the memory state at any past moment.
Queries use Neo4j datetime() comparisons on createdAtUtc properties.
- Zero build warnings in
src/ - Tests pass — both unit and (if applicable) integration
- No boundary violations — check dependency direction
- Cypher in constants files — not inline
- No IAgentMemory / StoreMessageAsync / AssembleContextAsync — these don't exist; use
IMemoryServiceand its actual methods - Docs updated alongside code changes (see §6 below)
Update documentation alongside code changes when:
- You add or change a public interface in
Abstractions - You add a new package or DI extension method
- You change configuration options or default values
- You add a new feature that users need to know about
Docs to update:
docs/architecture.md— for architectural changesdocs/schema.md— for graph schema changes (new node types, relationships, indexes)docs/getting-started.md— for configuration or DI registration changesREADME.md— for significant new capabilities
Use GitHub Issues for bug reports, feature requests, and questions. Include:
- .NET version and OS
- Neo4j version
- A minimal reproducible example if reporting a bug