This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
automem-evals is a recall-quality evaluation harness for the AutoMem server. It is NOT a general eval framework — every runner, scenario, and metric is shaped around the /recall HTTP endpoint of a locally running AutoMem stack.
The end goal is answering "does this ruleset (the set of recall_memory parameters an agent is instructed to use) surface the right memories?" by running the same scenarios across different rulesets against a seeded corpus with known ground-truth hits.
Official benchmark claims do not live here. automem remains the source of truth for official LoCoMo / LongMemEval harnesses, published baselines, and any benchmark numbers referenced in docs, CI, or release notes.
See docs/REPO_BOUNDARY.md for the repo split.
ruleset (JSON) ──► runner (Python) ──► corpus+manifest (JSONL/JSON)
│ │ ▲
│ └──── HTTP /recall ───────┘
▼ (localhost:8001)
recall params │
(phase 1/2/3) ▼
scenario → expected memory_ids
expected_hit_tags (in scenario)
map through manifest["scenario_to_memories"]
Each file defines phase_1_preferences / phase_2_task_context / phase_3_debugging parameter dicts. These mirror the two-phase recall convention documented in the global ~/.claude/CLAUDE.md. Comparing rulesets IS the primary experiment — iso_a..iso_j isolate individual knobs (limit, time_query, auto_decompose, tag gate, expand_relations) to attribute gains to specific changes.
The bare_tag_1m_v2 ruleset represents the currently-recommended defaults (90-day window, limit 20/30, auto_decompose). baseline_v1 represents the pre-v2 template for A/B comparison.
A scenario is {id, phase, query?, project_slug?, expected_hit_tags}. Scoring is indirect: expected_hit_tags are NOT memory tags — they are scenario IDs (e.g. TP-AUTH, DEBUG-CI) that the manifest resolves to memory_ids via scenario_to_memories. A memory can hit multiple scenarios by listing them in metadata.hits_scenarios at seed time.
corpus_v1.jsonl(78 memories, hand-authored) andcorpus_v2.jsonl(370 memories, parameterized generator) are the raw seed.*.embedded.jsonlis the same plus pre-computed Voyagevoyage-41024d vectors, produced bysnapshot_corpus.pyafter a slow first seed. Reseeding from the embedded snapshot is the fast, $0 path.*.manifest.jsonholds the bidirectional mapping (memory_to_scenarios/scenario_to_memories) required by scoring. It is produced at seed time and must match the memory_ids currently in the server.
Everything assumes Python 3.10+ (scripts use dict | None). Stdlib only — no requirements.txt, no venv needed.
# 0. Start the AutoMem stack (sibling repo, required — assumed to be at ../automem)
cd ../automem && docker compose up -d
# Verify: http://localhost:8001/health reports falkordb + qdrant up
# 1a. Fast reseed (v1, uses snapshotted embeddings, no API calls)
python3 scripts/seed_from_snapshot.py
python3 scripts/seed_associations.py # adds typed edges for expansion experiments
# 1b. Full v2 reseed (still cheap if snapshot exists)
python3 scripts/seed_corpus.py --corpus corpus_v2.jsonl # will skip embedding if server has key
python3 scripts/snapshot_corpus.py --corpus corpus_v2 # write snapshot for next reseed
# 2. Run a ruleset comparison (writes data/results/<timestamp>-comparison.md)
python3 runners/compare_rulesets.py --rulesets baseline_v1 bare_tag_1m_v2
python3 runners/compare_rulesets.py \
--rulesets baseline_v1 bare_tag_1m_v2 \
--scenarios session_start_v2 --manifest corpus_v2.manifest.json
# 3. Knob-isolation sweep (attributes v2's gain to individual parameters)
python3 runners/compare_rulesets.py --rulesets \
iso_a_baseline_all_off iso_b_bump_limit iso_c_bump_time iso_d_add_decompose \
iso_e_drop_tag_gate iso_f_add_expand iso_g_all_new_v2 \
iso_h_v2_no_gate iso_i_v2_no_gate_expand iso_j_v2_expand
# 4. Client-side graph expansion prototype
python3 runners/client_side_expand.py --ruleset bare_tag_1m_v2 --scenarios graph_expansion_v1Endpoint + token defaults: http://localhost:8001 and test-token. Override with --endpoint / --token. There is no production endpoint; this repo is local-only.
If you reset the AutoMem volumes (docker compose down -v), the manifest becomes stale and scoring collapses to all-zero — always re-run seed_from_snapshot.py (which rewrites the manifest) after a volume reset.
There is no built-in --scenario-id filter. Either:
- Edit the scenarios file to remove everything but the target entry, or
- Construct the
/recallcall by hand:runners/compare_rulesets.py:run_phaseshows the exact parameter-construction logic per phase.
- Tags are a hard gate on
/recall. A memory without a matching tag is excluded before scoring even if it is a perfect semantic match. This is whyscenarios[].project_slug+ruleset.tags_from: "project_slug"is the dominant knob. - Server-side
expand_relationsis a no-op under tag gating — the server filters expansion targets by the same tag filter.runners/client_side_expand.pyexists because of this; it walks inlineresults[].relations[]to approximate an ungated expansion pass. When experimenting with expansion, prefer the client-side runner over addingexpand_relations: trueto a ruleset. - Phase 1 uses tag-only, no query. Phase 2 uses semantic + project-gated + time-windowed. Phase 3 uses semantic + bugfix/solution tags. These shapes are baked into
runners/compare_rulesets.py:run_phase— if you add a new ruleset knob, also wire it through there. - Scenario IDs are conventional, not enforced.
TP-*= tensor-pipeline,DA-*= dashboard-app,PS-*= payment-service,SE-*= search-engine,OS-*= old-service,VID-*= video,DEBUG-*= phase-3,SS-*= session-start scenario (inscenarios/session_start_*.json),PREF-*= preference scope. Keep the prefix convention when adding memories/scenarios so the manifest stays readable. - The
videoslug is deliberately collision-prone in v2. Do not "fix" it by renaming — its purpose is to exercise the slug-collision case. - Memories live in AutoMem, not in this repo. Do not treat the JSONL as source of truth for what the server returns — server-side enrichment (summary, entity extraction) mutates fields after ingest. For debugging recall, always inspect
/memory/<id>or/recallresponse, not the seed file.
docs/experiments/STATUS.md— generated experiment dashboard; start here when orienting across hypotheses, results, decisions, active worktrees, and undocumented run artifacts. Updatedocs/experiments/registry.json, then runpython3 scripts/experiment_index.pybefore ending a substantial experiment session.docs/session_*.md— dated narrative notes per session; read these for context when revisiting an old result.data/results/SUMMARY-*.md— headline writeups per major experiment.data/results/<timestamp>-comparison.md— raw per-run report produced bycompare_rulesets.py. These accumulate; the SUMMARY files are the curated view.
The global file is authoritative. Two project-specific notes:
- Project slug for AutoMem memory tags is
automem-evals(bare, noproject/prefix). - Do not store memories about transient per-run findings (specific hit counts for a ruleset on a given day). Do store design decisions, surprising non-obvious behaviors of the AutoMem server exposed by evals, and rule-of-thumb conclusions that survive re-runs.