README.md

July 8, 2026 · View on GitHub

Noema logo

Noema

Inspectable, local-first memory for coding agents.

CI License: MIT Rust 2021 Status: early implementation MCP stdio

Quick Start · CLI · Recall · Storage · Integrations · Development

Noema is a local-first, non-vector memory system for coding agents. It stores durable agent memory as inspectable files, keeps new memories behind a review queue, recalls context with lexical signals instead of embeddings, and is designed to work across hosts such as zode, Codex, Claude Code, and MCP-capable agent runtimes.

Use Noema when you want agent memory that is easy to audit, easy to delete, safe to run locally, and portable across terminal, CLI, MCP, and enterprise host environments.

Project Status

Noema is in early implementation. The P0-P6 architecture is represented in this repository, but public APIs, file formats, and integration contracts may still change before a stable release.

At a Glance

AreaNoema's approach
Memory formatMarkdown records with structured JSON frontmatter.
RecallLexical BM25, PageIndex browsing, and graph expansion fused without embeddings.
ReviewHippocampus-style candidate queue before durable cortex memory.
StoragePlain local files under ~/.agent-memory/, with NOEMA_ROOT for isolated runs.
IntegrationsCLI, MCP stdio, zode host workflows, and an enterprise server boundary.
PrivacySensitivity caps, payload-free audits, tombstones, and restore-time deletion checks.

Contents

Translations

English is the canonical README. Concise localized READMEs are available in 14 additional languages:

LanguageREADME
EnglishREADME.md
简体中文README.zh-CN.md
繁體中文README.zh-TW.md
日本語README.ja.md
한국어README.ko.md
EspañolREADME.es.md
FrançaisREADME.fr.md
DeutschREADME.de.md
Português (Brasil)README.pt-BR.md
РусскийREADME.ru.md
العربيةREADME.ar.md
हिन्दीREADME.hi.md
Bahasa IndonesiaREADME.id.md
TürkçeREADME.tr.md
Tiếng ViệtREADME.vi.md

ZSeven-W Ecosystem

Noema is part of a broader ZSeven-W agent and design-tooling stack:

ProjectWhat it is
zodeOpen-source, AI-native coding assistant for the terminal, with a Rust TUI, multi-provider model support, sandboxed tools, sessions, agents, skills, and MCP integrations.
agent-rsPure-Rust async runtime for building LLM agents, with provider streaming, tool dispatch, structured permissions, MCP support, cost tracking, and optional coding tools.
jianRust-native cross-platform UI framework where a single .op document can become a live, hot-reloading, AI-controllable native app.
OpenPencilOpen-source AI-native vector design tool for prompt-to-canvas workflows, concurrent agent teams, Design-as-Code, built-in MCP, and multi-platform export.

Why Noema

Most agent memory systems are built around vector databases. That is useful for semantic search, but it can make memory opaque, hard to audit, hard to delete, and difficult to run safely in enterprise environments.

Noema takes a different path:

  • No vectors or embeddings: recall is lexical, explainable, and reproducible.
  • Local-first storage: the default root is ~/.agent-memory/, with an optional NOEMA_ROOT override for tests and isolated workspaces.
  • Human review by default: new candidates enter a hippocampus-style queue before becoming durable cortex memory.
  • Agent-neutral protocol: the same memory store can be used by the CLI, zode, MCP clients, and future host adapters.
  • Enterprise-aware from the start: tenant boundaries, sensitivity levels, audit events, ACL policy hooks, KMS metadata policy, and S3-compatible cold offload are part of the design rather than later patches.

Features

  • Tenant-scoped file layout under ~/.agent-memory/tenants/<tenant>/.
  • Markdown memory records with structured JSON frontmatter.
  • Event-sourced review queue using hippocampus/inbox.jsonl and hippocampus/decisions.jsonl.
  • Candidate decisions: accept, reject, edit, and merge.
  • Lexical recall that returns a MemoryPack with scores and source metadata.
  • explain support for understanding why a memory matched a query.
  • Automatic rejection of secret candidates before review.
  • Personal-mode sensitivity cap: public and internal only.
  • Payload-free audit events for review and memory lifecycle operations.
  • Vacuum support for compacting review history.
  • Forget/tombstone flow with restore-time deletion manifest checks.
  • S3-compatible cold offload scaffolding with local fake-store tests.
  • MCP stdio tool surface.
  • Minimal enterprise server boundary for signed principals and policy checks.

Repository Layout

.
+-- crates/
|   +-- noema-core/      # protocol types, paths, storage, recall, review, policy
|   +-- noema-cli/       # `noema` command-line interface
|   +-- noema-mcp/       # MCP stdio tool surface
|   +-- noema-server/    # enterprise boundary and status service
+-- tests/               # CLI, MCP, enterprise, and offload contract tests
+-- Cargo.toml
+-- README.md

Install From Source

Noema is currently distributed from source.

Requirements:

  • Rust stable toolchain
  • Git
git clone https://github.com/ZSeven-W/noema.git
cd noema
cargo build --workspace

Run the CLI from the workspace:

cargo run -p noema -- --help

Install it into your Cargo bin directory:

cargo install --path crates/noema-cli

Quick Start

Use a temporary root while trying Noema:

export NOEMA_ROOT="$(mktemp -d)"

cargo run -p noema -- init
cargo run -p noema -- remember "Prefer Rust for Noema implementation work."
cargo run -p noema -- review

If remember auto-accepts the candidate, it will print an accepted memory id. If it queues the candidate, accept it from the review queue:

cargo run -p noema -- accept cand_xxxxx

For explicit host-agent memory writes that should be available immediately:

cargo run -p noema -- remember "Prefer Rust for Noema implementation work." --accept

Search and inspect recall:

cargo run -p noema -- recall "Rust implementation"
cargo run -p noema -- search "Rust implementation"
cargo run -p noema -- explain mem_xxxxx --query "Rust implementation"

For normal use, omit NOEMA_ROOT and Noema will use:

~/.agent-memory/

CLI Overview

CommandPurpose
noema initCreate the local memory layout and config.
noema remember <text>Create a memory candidate.
noema remember <text> --acceptPersist an explicit memory immediately.
noema reviewList pending candidates.
noema edit <candidate> --body ... --reason ...Revise a pending candidate before acceptance.
noema merge <candidate> <memory> --reason ...Drop a duplicate candidate in favor of an existing memory.
noema accept <candidate>Persist a pending candidate as memory.
noema reject <candidate> --reason ...Reject a pending candidate.
noema recall <query>Print a markdown memory pack with full memory text.
noema search <query>Search matching memory ids and scores.
noema explain <memory> --query ...Explain a recall score.
noema vacuumCompact terminal review events into snapshots and archives.
noema sleep [--llm]Move stale or low-utility memory toward deeper storage.
noema benchRun a synthetic recall benchmark and print README-ready tables.
noema bench --mem0-targetsPrint the Mem0 benchmark scores Noema must exceed.
noema bench --mem0-result <file>Summarize a Mem0 benchmark result JSON file.
noema bench --locomo-dataset <file>Summarize a LOCOMO dataset file.
noema bench --locomo-evidence <file>Run Noema LOCOMO evidence retrieval. Use --locomo-memory-source raw|observation|raw-plus-observation|fact-layer|raw-plus-fact-layer to compare layers.
noema bench --locomo-evidence <file> --locomo-predict-output <file>Write a mem0-like predict/proxy JSON file with retrieval contexts and evidence-hit cutoff results.
noema bench --locomo-predict-input <file>Continue from an existing LOCOMO predict JSON without recomputing retrieval.
noema bench --locomo-evidence <file> --locomo-answer-tasks-output <file>Write host-LLM answer-generation tasks from the same top-k LOCOMO retrieval context.
noema bench --locomo-predict-input <file> --locomo-retry-answer-tasks-output <file>Write only missing, empty, or host-failed LOCOMO answer tasks.
noema bench --locomo-predict-input <file> --locomo-answer-tasks-input <file> --locomo-retention-output <file>Audit budgeted answer-task prompt retention against the full predict retrieval context.
noema bench --locomo-evidence <file> --locomo-judge-tasks-output <file>Convert answer results into judge tasks for LOCOMO correctness grading.
noema bench --locomo-predict-input <file> --locomo-retry-judge-tasks-output <file>Write only missing, malformed, or host-failed LOCOMO judge tasks.
noema bench --locomo-evidence <file> --locomo-final-output <file>Combine predict, answer, and judge results into final LOCOMO metrics.
noema bench --locomo-predict-input <file> --locomo-target-output <file>Compare a judged LOCOMO final result against the Mem0 LoCoMo target.
noema bench --locomo-predict-input <file> --locomo-require-beats-mem0Exit non-zero unless the judged LOCOMO score exceeds Mem0's LoCoMo score.
noema bench --locomo-predict-input <file> --locomo-status-output <file>Summarize answer/judge completeness for an offline LOCOMO run.
noema bench --locomo-predict-input <file> --locomo-report-output <file>Combine proxy, prompt retention, and answer/judge readiness into one run report.
noema bench --locomo-predict-input <file> --locomo-host-manifest-input <file> --locomo-report-output <file>Embed zode host-run provenance in the LOCOMO run report.
noema bench --locomo-predict-input <file> --locomo-fail-if-incompleteExit non-zero until the LOCOMO answer and judge artifacts are ready for final scoring.
noema forget <memory> [--hard]Tombstone or hard-erase a memory.
noema offload statusShow cold-offload status.
noema offload runRun cold-offload processing.
noema restore <snapshot-or-id>Restore while applying deletion manifests.
noema doctorCheck local store health.
noema reindexRebuild local indexes.

Recall Paths

Noema exposes several deterministic recall paths:

  • recall: default agent context injection. It uses Fusion Recall: lexical BM25, PageIndex entity/topic browsing, and graph expansion are combined with Reciprocal Rank Fusion, so memories confirmed by several sources rank above single-source hits. Per-entity diversity limits apply, but entities the query names are exempt and capped candidates refill free slots. The memory pack is budget-driven: it fills until the token budget is spent and skips oversized memories instead of stopping at them.
  • search: diagnostic lexical recall. Use it when you need reproducible BM25 score explanations without associative expansion.
  • browse: PageIndex navigation. Use it when a query names an entity, topic, or project and related memories may not share the same words as the query.
  • recall_graph: deterministic multi-hop recall from lexical seeds through links and shared entities.
  • neighbors: one-hop graph inspection for agent-driven memory exploration.

The default agent path should call recall first. If the answer depends on a person, project, or topic profile, hosts should call browse or catalog before asking the model to answer. If the answer spans several connected memories, hosts should call recall_graph or step through neighbors.

Storage Model

Noema keeps storage boring on purpose. The hot path is made of directories, Markdown files, JSONL event logs, and lock files that are easy to inspect and back up.

~/.agent-memory/
+-- config.toml
+-- manifests/
+-- tenants/
    +-- personal/
        +-- tenant.lock
        +-- audit.jsonl
        +-- users/
        |   +-- <user-id>/
        |       +-- memories/
        +-- projects/
        |   +-- <project-id>/
        |       +-- memories/
        +-- hippocampus/
        |   +-- inbox.jsonl
        |   +-- decisions.jsonl
        |   +-- snapshots/
        |   +-- archive/
        +-- indexes/
        +-- trash/
        +-- cold/

The default personal tenant is intentionally simple. Enterprise deployments can add signed principals, stronger tenant boundaries, ACL policy, and external cold storage without changing the local memory protocol.

Memory Lifecycle

candidate
  -> pending review
  -> accepted memory
  -> recalled cortex memory
  -> deep memory when stale
  -> resurrected for one request when relevant
  -> deep again unless confirmed
  -> forgotten or hard-erased when required

Noema separates capture from persistence. Hosts can submit candidates automatically, but the review queue remains the standard control point. A configuration can make review more automatic, while sensitivity ceilings and secret rejection still apply.

Security And Privacy

Noema treats memory as potentially sensitive infrastructure.

  • secret candidates are rejected before they enter review.
  • Personal mode rejects confidential and restricted writes, so users do not create memory they cannot recall.
  • Recall filters memory above the principal's clearance.
  • Audit events avoid storing raw memory payloads.
  • Redacted or summary-only recall requires precomputed safe variants; Noema does not rely on runtime prose redaction for sensitive data.
  • Sensitive full-text indexing is isolated from normal turn recall.
  • Hard erasure must cascade across hot memory, deep memory, local stubs, indexes, cold payload metadata, and restore deletion manifests.
  • Restores apply deletion manifests so old snapshots do not silently resurrect erased records.

These controls are intentionally conservative. The personal CLI is useful on its own, but the file protocol and policy model are built for multi-tenant enterprise use.

Integrations

CLI

noema is the reference interface and the best place to inspect behavior while the project is early.

For host agents that do not use MCP, install the CLI and the bundled skill:

cargo install --path crates/noema-cli
cp -R skills/noema-memory "${CODEX_HOME:-$HOME/.codex}/skills/"

The skill uses:

noema recall "<user request>"
noema remember "<stable fact or preference>" --accept

recall prints a markdown memory pack with full memory text for model context. remember --accept is for explicit host-agent memory writes; plain remember still follows the review queue policy.

MCP

noema-mcp exposes a stdio JSON-RPC surface for MCP-capable hosts:

cargo install --path crates/noema-mcp
noema-mcp

Generic JSON MCP clients can point at the stdio server:

{
  "mcpServers": {
    "noema": {
      "command": "noema-mcp"
    }
  }
}

Codex-style TOML MCP config can use the same command:

[mcp_servers.noema]
command = "noema-mcp"

The MCP contract is tested end-to-end: tool listing, noema_remember, and noema_recall must round-trip through an isolated Noema root.

zode

Noema is designed to be consumed by zode as a native memory backend while remaining a standalone repository. zode can depend on it through a submodule, workspace dependency, or package release without making memory storage zode-specific.

Enterprise Server

noema-server contains the beginning of the enterprise trust boundary: signed principal claims, tenant-aware policy checks, and service status endpoints.

S3-Compatible Cold Offload

Noema's primary store is local, but cold memory can be offloaded to an S3-compatible backend to control disk growth. The cold-offload design keeps local metadata and restore safeguards, including deletion manifests for erased records.

Current implementation includes the storage abstraction and fake filesystem backend used by tests. Production S3 wiring should be treated as an integration target while APIs are still unstable.

Benchmarks

Mem0 Benchmark Targets

Noema is being built against the public mem0ai/memory-benchmarks suite at commit 4b61c5d31b9c668a12b4f5e78064248a02c82d2b.

That suite evaluates memory systems with an Ingest -> Search -> Evaluate pipeline across LOCOMO, LongMemEval, and BEAM. The target is not to reproduce Mem0's architecture; Noema intentionally stays local-first and non-vector. The target is to beat the published scores while keeping Noema's auditability, deletion, tenant isolation, and sensitivity controls intact.

Current external reference scores:

cargo run -p noema -- bench --mem0-targets
BenchmarkMetricMem0 scoreNoema must exceedNotes
LoCoMooverall score92.5> 92.5multi-session dialogue memory
LongMemEvaloverall score94.4> 94.4long-term memory questions
BEAM 1Maverage score64.1> 64.11M-token memory ability benchmark
BEAM 10Maverage score48.6> 48.610M-token memory ability benchmark

These are external Mem0 baselines. Current judged Noema results:

BenchmarkCutoffRunnerNoema scoreMem0 scoreMarginCorrect / totalStatus
LoCoMotop_200zode host runner, v7 96k prompts92.662392.5+0.16231427 / 1540exceeds Mem0 and Noema target
LongMemEval---94.4--not yet run
BEAM 1M---64.1--not yet run
BEAM 10M---48.6--not yet run

The LoCoMo run used the Mem0 benchmark data at commit 4b61c5d31b9c668a12b4f5e78064248a02c82d2b, Noema's raw-plus-fact-layer top-200 retrieval export, a 96k-character answer prompt budget, and zode as the offline answerer/judge host runner. Noema is considered competitive for LoCoMo top-200 only; LongMemEval and BEAM still need comparable runs before the broader benchmark goal is complete.

Noema can also summarize checked-in Mem0 result JSON files from that repository:

cargo run -p noema -- bench --mem0-result results/platform/beam_1m_results.json

Example summary for the Mem0 BEAM 1M result at the referenced commit:

CutoffMetricScoreTotal
top_200avg_score64.1700

Noema's benchmark adapter should map the mem0 suite as follows:

  • Ingest: convert benchmark conversations into Noema memory candidates and durable memory records, preserving user/session/time metadata.
  • Search: run Noema lexical recall at the same top-k cutoffs used by the mem0 suite, especially top 50 and top 200.
  • Evaluate: support predict-only retrieval first, then answerer/judge mode through a host LLM so Noema can report comparable accuracy/pass-rate scores.
  • Report: group results by LOCOMO category, LongMemEval question type, and BEAM memory ability type.

Current LOCOMO adapter status:

cargo run -p noema -- bench --locomo-dataset /tmp/locomo10.json
DatasetConversationsSessionsTurnsQuestionsEvaluable questions
LOCOMO-1010272588219861540

LOCOMO category counts for the mem0-evaluated set:

CategoryQuestions
multi-hop282
open-domain96
single-hop841
temporal321

Noema can now run an evidence-retrieval proxy over LOCOMO. The raw mode uses original conversation turns with previous/next turn context plus session-level hippocampal episode memories; the fact-layer mode adds observation-derived fact memories plus speaker-level summary memories with provenance back to the source dia_ids.

cargo run -p noema -- bench --locomo-evidence /tmp/locomo10.json --top-k 200 --locomo-memory-source raw-plus-fact-layer

Current proxy result:

SourceTop KAny evidence hitAll evidence hitQuestions with resolved evidence
raw5099.7%97.4%1536
raw20099.9%99.9%1536
raw-plus-fact-layer5099.9%99.0%1536
raw-plus-fact-layer200100.0%99.9%1536

Noema can also export a mem0-like predict/proxy JSON file for the next answerer/judge phase, plus a per-question directory that matches mem0 --evaluate-only expectations:

cargo run -p noema -- bench \
  --locomo-evidence /tmp/locomo10.json \
  --top-k 200 \
  --locomo-memory-source raw-plus-fact-layer \
  --locomo-predict-output /tmp/noema-locomo-raw-plus-fact-layer-top200.json \
  --locomo-predict-dir /tmp/noema-locomo-mem0-predict-top200 \
  --locomo-answer-prompt-char-budget 96000 \
  --locomo-answer-tasks-output /tmp/noema-locomo-answer-tasks-top200-budget96k.jsonl

The current top-200 unified export is about 323 MB and contains all 1540 mem0 evaluated category 1-4 questions. Questions whose evidence IDs cannot be mapped to a Noema memory are kept in the export and counted as proxy misses, so mem0 --evaluate-only can run without missing per-question files. LOCOMO evidence fields that pack multiple dia_ids into one semicolon- or space-separated string are split before scoring and task export, and leading zero forms such as D30:05 are normalized to the matching turn id.

ArtifactSourceTop KProxy scoreCorrect / total
/tmp/noema-locomo-raw-plus-fact-layer-top200.jsonraw-plus-fact-layer20099.7%1536 / 1540
/tmp/noema-locomo-mem0-predict-top200/raw-plus-fact-layer20099.7%1540 question files + summary
/tmp/noema-locomo-answer-tasks-top200-budget96k.jsonlraw-plus-fact-layer200answerer input1540 tasks

The unbudgeted answer-task JSONL is about 201 MB after query-aware episode compaction. With --locomo-answer-prompt-char-budget 96000, the top-200 answer-task JSONL is about 148 MB and keeps the answerer prompt under roughly 96k characters per question without changing the predict/evidence-proxy export. Each line contains a custom_id, the LOCOMO question metadata, and a host-LLM messages payload built from the same top-k retrieval context. It is intended for zode, Codex, Claude Code, or an external batch runner to generate answers before a judge pass computes the final LOCOMO accuracy.

Prompt budgets are a cost/retention tradeoff. Noema can audit a budgeted answer task file against the full predict JSON:

cargo run -p noema -- bench \
  --locomo-predict-input /tmp/noema-locomo-raw-plus-fact-layer-top200.json \
  --top-k 200 \
  --locomo-answer-tasks-input /tmp/noema-locomo-answer-tasks-top200-budget96k.jsonl \
  --locomo-retention-output /tmp/noema-locomo-answer-retention-top200-budget96k.json

The conservative evidence-ID audit maps each budgeted task back to the first retrieval_results_in_prompt records and checks whether LOCOMO D... evidence IDs still appear in those retained memories. It is stricter than the evidence proxy because fact-layer summary hits do not always carry raw D... IDs. The same retention JSON includes prompt_summary, so prompt character totals, estimated prompt tokens, retained retrieval counts, omitted retrieval counts, and truncation counts can be reproduced without a separate zode dry-run.

Answer prompt budgetTask file bytesEstimated prompt tokensAvg retained retrievalsID any-hit / evaluableID all-hit / evaluableBaseline any/all lost
48k chars72,455,09117,805,31932.81515 / 15361440 / 153619 / 91
64k chars98,329,44624,239,27458.21529 / 15361487 / 15365 / 44
96k chars, v7148,381,41536,611,760155.31534 / 15361529 / 15360 / 2

With zode as the host LLM runner:

python3 /Users/kayshen/Workspace/ZSeven-W/zode/benchmarks/noema_locomo.py \
  --tasks /tmp/noema-locomo-answer-tasks-top200-budget96k.jsonl \
  --output /tmp/noema-locomo-answer-results.jsonl \
  --zode-config-dir ~/.zode \
  --jobs 1 \
  --resume \
  --retry-empty \
  --retry-failed \
  --summary-output /tmp/noema-locomo-answer-results.summary.json \
  --manifest-output /tmp/noema-locomo-answer-run.manifest.json \
  --stop-on-provider-blocker \
  --retries 2

For long runs, Noema can also emit a compact retry-only answer task file from the current append-only result JSONL:

cargo run -p noema -- bench \
  --locomo-predict-input /tmp/noema-locomo-raw-plus-fact-layer-top200.json \
  --top-k 200 \
  --locomo-answer-prompt-char-budget 96000 \
  --locomo-answer-results /tmp/noema-locomo-answer-results.jsonl \
  --locomo-retry-answer-tasks-output /tmp/noema-locomo-answer-tasks-retry-top200-budget96k.jsonl

That retry file preserves the original custom_ids and contains only missing, empty, or host-failed answers, so zode can append recovered results back into the same answer-results file. In the final v7 96k run, the first full answer pass produced 16 retryable rows; a resume pass with --retry-empty --retry-failed recovered all of them, leaving 1540 valid answers and zero retryable answer rows.

Noema can also combine the current proxy, prompt-retention audit, and answer/judge readiness into a single run report:

cargo run -p noema -- bench \
  --locomo-predict-input /tmp/noema-locomo-raw-plus-fact-layer-top200.json \
  --top-k 200 \
  --locomo-answer-tasks-input /tmp/noema-locomo-answer-tasks-top200-budget96k.jsonl \
  --locomo-answer-results /tmp/noema-locomo-answer-results-zode-top200.jsonl \
  --locomo-judge-results /tmp/noema-locomo-judge-results-empty.jsonl \
  --locomo-host-manifest-input /tmp/noema-locomo-answer-run.manifest.json \
  --locomo-report-output /tmp/noema-locomo-run-report-top200-budget96k.json

The current v7 96k full report records predict_proxy.overall.accuracy=99.74025974025976, prompt_retention.overall.retained_any_evidence_hits=1534, prompt_retention.overall.retained_all_evidence_hits=1529, completion.final_ready=true, completion.blocked_reason=ready, and a target_verdict with score=92.66233766233766, correct=1427, total=1540, exceeds_mem0=true, and meets_noema_target=true. When --locomo-host-manifest-input is provided, the report embeds the zode runner manifest under host_runner, adding provider-run provenance such as unrun_due_to_provider_blocker.

After answer generation, Noema can convert answer results into judge-task JSONL:

cargo run -p noema -- bench \
  --locomo-predict-input /tmp/noema-locomo-raw-plus-fact-layer-top200.json \
  --top-k 200 \
  --locomo-answer-results /tmp/noema-locomo-answer-results.jsonl \
  --locomo-judge-tasks-output /tmp/noema-locomo-judge-tasks-top200.jsonl

Answer result lines can use { "custom_id": "...", "answer": "..." }, generated_answer, or a common batch response shape with response.body.choices[0].message.content. The generated judge tasks ask for a JSON { "reasoning": "...", "label": "CORRECT|WRONG" } response and preserve the original question_id/cutoff in custom_id.

Run the judge tasks through zode the same way:

python3 /Users/kayshen/Workspace/ZSeven-W/zode/benchmarks/noema_locomo.py \
  --tasks /tmp/noema-locomo-judge-tasks-top200.jsonl \
  --output /tmp/noema-locomo-judge-results.jsonl \
  --zode-config-dir ~/.zode \
  --jobs 1 \
  --resume \
  --retry-empty \
  --retry-failed \
  --summary-output /tmp/noema-locomo-judge-results.summary.json \
  --manifest-output /tmp/noema-locomo-judge-run.manifest.json \
  --stop-on-provider-blocker \
  --retries 2

The zode runner summary is a quick host-side count of latest answer/judge rows. It can inspect a task file without an output file or host LLM call:

python3 /Users/kayshen/Workspace/ZSeven-W/zode/benchmarks/noema_locomo.py \
  --dry-run \
  --tasks /tmp/noema-locomo-answer-tasks-top200-budget96k.jsonl \
  --summary-output /tmp/noema-locomo-answer-dry-run.summary.json \
  --manifest-output /tmp/noema-locomo-answer-dry-run.manifest.json

It can also summarize existing result files without a task file or host LLM call:

python3 /Users/kayshen/Workspace/ZSeven-W/zode/benchmarks/noema_locomo.py \
  --summary-only \
  --fail-on-retryable \
  --output /tmp/noema-locomo-answer-results.jsonl \
  --summary-output /tmp/noema-locomo-answer-results.summary.json \
  --manifest-output /tmp/noema-locomo-answer-summary.manifest.json

With --fail-on-retryable, the runner exits with status 2 when latest results still contain ordinary retryable answer or judge rows; if those retryables include a provider blocker such as http_402_payment_required, summary-only exits with status 3. For provider-balance or billing-sensitive retry batches, run the zode runner with --jobs 1 --stop-on-provider-blocker; it stops at the first blocker such as http_402_payment_required, writes the partial JSONL and manifest, and exits with status 3.

Noema can gate the full offline benchmark state across predict, answer, and judge artifacts:

cargo run -p noema -- bench \
  --locomo-predict-input /tmp/noema-locomo-raw-plus-fact-layer-top200.json \
  --top-k 200 \
  --locomo-answer-tasks-input /tmp/noema-locomo-answer-tasks-top200-budget96k.jsonl \
  --locomo-answer-results /tmp/noema-locomo-answer-results-zode-top200.jsonl \
  --locomo-judge-results /tmp/noema-locomo-judge-results-empty.jsonl \
  --locomo-host-manifest-input /tmp/noema-locomo-answer-summary-zode-top200.manifest.json \
  --locomo-fail-if-incomplete

Before answer and judge artifacts are complete, the gate returns an error such as LOCOMO run incomplete: blocked_reason=host_provider_blocked next_action=resolve_provider_blocker retryable=0 provider_blocker_reason=http_402_payment_required while a provider balance blocker is unresolved. Once answer and judge artifacts are complete, the same gate prints that the run is ready for final scoring.

--manifest-output records the host-run provenance needed for reproducible benchmark reports: task/result paths, zode binary, provider/model names, retry settings, run/skipped counts, tasks_total, pending_before_run, unrun_due_to_provider_blocker, task input size stats, prompt character distribution, a rough ceil(prompt_chars / 4) prompt-token estimate, the same latest-row summary, provider-blocker status, and answer failure-reason buckets such as http_402_payment_required. It intentionally does not include provider API keys. Use --zode-config-dir to run against an isolated zode config directory instead of whichever global config is active in the shell.

The current v7 96k-budget answer run manifest records task_file_bytes=148381415, tasks_loaded=1540, and prompt chars p50=95738, p95=95959, max=96000. It estimates prompt input at 36611760 tokens total, with p50=23935, p95=23990, and max=24000 per task using the runner's 4 chars/token estimate. It also records Noema prompt-retention stats: retrieval_results_in_prompt.mean=155.3422077922078, p95=192, and truncated_memories.total=0. For comparison, the unbudgeted compacted top-200 task file records task_file_bytes=200747951, prompt chars p50=130719, p95=147523, max=162186, and about 49778930 estimated prompt tokens total. The current full judge-task artifact is /tmp/noema-locomo-judge-tasks-zode-top200-v7-96k-full.jsonl; its zode manifest records task_file_bytes=2608433, tasks_loaded=1540, prompt chars total=1683047, p50=1070, p95=1323, max=2110, and estimated prompt input tokens total=421327, p50=268, p95=331, max=528.

Use Noema's status JSON below as the authoritative readiness check because it also knows the predict set and expected task ids.

If a judge run is interrupted or returns malformed host output, emit retry-only judge tasks from the current answer and judge results:

cargo run -p noema -- bench \
  --locomo-predict-input /tmp/noema-locomo-raw-plus-fact-layer-top200.json \
  --top-k 200 \
  --locomo-answer-results /tmp/noema-locomo-answer-results.jsonl \
  --locomo-judge-results /tmp/noema-locomo-judge-results.jsonl \
  --locomo-retry-judge-tasks-output /tmp/noema-locomo-judge-tasks-retry-top200.jsonl

At any point, write a benchmark-status JSON report from the current predict, answer, and optional judge outputs:

cargo run -p noema -- bench \
  --locomo-predict-input /tmp/noema-locomo-raw-plus-fact-layer-top200.json \
  --top-k 200 \
  --locomo-answer-results /tmp/noema-locomo-answer-results.jsonl \
  --locomo-status-output /tmp/noema-locomo-status-top200.json

The status report uses append-only/latest-row semantics for result JSONL, matching the zode runner's --resume behavior. It counts valid answers, empty answers, host failures, missing answers, valid judge labels, malformed/host-failed judge rows, and whether the run is ready for final scoring. Add --locomo-judge-results <file> once judge results exist.

After the judge pass finishes, Noema can recompute the final LOCOMO metrics_by_cutoff from the judge labels:

cargo run -p noema -- bench \
  --locomo-predict-input /tmp/noema-locomo-raw-plus-fact-layer-top200.json \
  --top-k 200 \
  --locomo-answer-results /tmp/noema-locomo-answer-results.jsonl \
  --locomo-judge-results /tmp/noema-locomo-judge-results.jsonl \
  --locomo-final-output /tmp/noema-locomo-final-results-top200.json

The final-output step requires complete answer and judge results for every exported evaluation. It preserves the original retrieval context, replaces the proxy cutoff result with CORRECT/WRONG judge labels, and marks the output as eval_mode = "answerer_judge_offline".

When the judged result is ready, Noema can write and enforce the Mem0 target verdict:

cargo run -p noema -- bench \
  --locomo-predict-input /tmp/noema-locomo-raw-plus-fact-layer-top200.json \
  --top-k 200 \
  --locomo-answer-results /tmp/noema-locomo-answer-results.jsonl \
  --locomo-judge-results /tmp/noema-locomo-judge-results.jsonl \
  --locomo-target-output /tmp/noema-locomo-target-verdict-top200.json \
  --locomo-require-beats-mem0

The verdict records the judged score, Mem0's LoCoMo target (92.5), Noema's strict target (92.6), and booleans for exceeds_mem0 and meets_noema_target. The combined run report embeds the same verdict under target_verdict whenever completion.final_ready=true.

Current zode-hosted full run status for the top-200 export:

ArtifactTotalValid answersValid judge labelsCorrectWrongRetryable answersRetryable judgesFinal ready
/tmp/noema-locomo-target-verdict-zode-top200-v7-96k-full.json154015401540142711300yes

The result files are append-only/latest-row JSONL. The final answer summary has rows=1556 because it includes the initial 16 retryable answer rows plus the successful resume rows, but unique=1540, answers.valid=1540, and answers.retryable=0. The judge summary has rows=1540, unique=1540, judges.correct=1427, judges.wrong=113, and judges.retryable=0.

The raw-plus-fact-layer evidence proxy remains useful for retrieval debugging, but the reported LoCoMo score above is the final answerer/judge offline score for the exported 1540 questions. It is directly compared against the Mem0 LoCoMo top-200 reference score of 92.5 from the referenced benchmark commit.

Local Recall Latency

Noema includes a small synthetic benchmark runner for recall-path tracking:

cargo run --release -p noema -- bench --memories 1000 --queries 8 --iterations 50

The benchmark creates a temporary Noema root, writes synthetic Markdown memory records, and measures two paths:

  • noema_engine_recall: reuse one NoemaEngine and call recall.
  • zode_turn_injection_equivalent: mirror zode's current memory injection path by constructing an engine from NOEMA_ROOT, recalling, and rendering the MemoryPack to Markdown for each turn. This does not include provider/model latency.

Measured on 2026-06-21 with a release build on macOS 26.5.1, Apple M2 Pro, rustc 1.95.0, cargo 1.95.0.

Dataset: 1000 generated memories, 8 queries, 50 iterations, 400 measured operations per scenario, 150262 generated memory-body bytes.

ScenarioOperationsTotal msMean us/opp50 usp95 us
noema_engine_recall4009391.78823479.47022976.08425944.042
zode_turn_injection_equivalent40010524.22826310.57023319.33446983.917

Phase breakdown:

ScenarioPhaseOperationsTotal msMean us/op
noema_engine_recallload_memories4008153.37320383.432
noema_engine_recallscore_memories4001143.6442859.110
noema_engine_recallbuild_pack40043.103107.756
zode_turn_injection_equivalentcreate_engine4000.1010.252
zode_turn_injection_equivalentload_memories4009255.37223138.431
zode_turn_injection_equivalentscore_memories4001170.8792927.197
zode_turn_injection_equivalentbuild_pack40043.847109.619
zode_turn_injection_equivalentrender_markdown4002.0855.211

These numbers measure the current file-backed lexical recall path. They are a baseline for future work on cached indexes, zode-native turn integration, and cold-store hydration behavior.

Development

Run the standard checks:

cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace

Run a local smoke test:

export NOEMA_ROOT="$(mktemp -d)"
cargo run -p noema -- init
cargo run -p noema -- remember "Prefer Rust for Noema."
cargo run -p noema -- review
cargo run -p noema-mcp <<< '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Contributing

Noema is early and intentionally file-first, so small, well-scoped contributions are useful. Good places to help:

  • Reproducible CLI or MCP issues with the command, expected behavior, and actual output.
  • Recall quality cases where lexical, PageIndex, or graph recall should rank a memory differently.
  • Host integration notes for zode, Codex, Claude Code, MCP clients, and other agent runtimes.
  • Documentation fixes, examples, and local-first deployment notes.
  • Focused tests for review, recall, offload, restore, and policy behavior.

Before opening a PR, run the standard checks from Development. If you change file formats or integration contracts, call that out clearly in the PR description because those surfaces are still stabilizing.

Roadmap

  • Stabilize the file protocol and frontmatter schema.
  • Harden MCP and host integration contracts.
  • Expand zode-native review and recall workflows.
  • Complete production S3-compatible offload configuration.
  • Add richer enterprise policy management and tenant administration.
  • Publish crate/package releases after the storage contract is stable.

License

MIT. See the workspace package metadata in Cargo.toml.