From 3d211abfc86df7d5b316092ebc781ce52d02cf6b Mon Sep 17 00:00:00 2001 From: "Mani Saint-Victor, MD" Date: Wed, 6 May 2026 16:44:22 -0400 Subject: [PATCH 1/5] docs: add standalone Elume quickstart --- AGENTS.md | 49 ++++++++++++++++++ CLAUDE.md | 1 + GEMINI.md | 1 + README.md | 7 +++ docs/quickstart.md | 122 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 180 insertions(+) create mode 100644 AGENTS.md create mode 120000 CLAUDE.md create mode 120000 GEMINI.md create mode 100644 docs/quickstart.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..3daee3c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,49 @@ +# Elume Agent Instructions + +This is the canonical instruction file for agents working in this repository. +`CLAUDE.md` and `GEMINI.md` should be symlinks to this file. + +## Project Frame + +- ALWAYS treat Elume as an independent Python library and cognitive substrate. +- NEVER frame Elume as a Dionysus Core component or require Dionysus knowledge for first-use documentation. +- ALWAYS make the primary quickstart a clean, standalone tutorial that runs without Dionysus, FastAPI, Graphiti, Neo4j, MemEvolve, or any agent framework. +- NEVER list adjacent systems in beginner-facing quickstart copy just to say they are not required. Say only what the user needs. +- ALWAYS place Dionysus as an optional downstream demonstration or advanced integration example, subordinate to the library-first user path. +- ALWAYS verify public API examples against the local repo before publishing tutorial claims. + +## Code And Documentation Work + +- ALWAYS check the worktree before editing and preserve unrelated local files. +- ALWAYS use existing Elume APIs and tests as the source of truth for examples. +- NEVER claim release readiness, test counts, or package status from old notes without refreshing live repo state. +- For docs-only changes, run at least `git diff --check`; when a tutorial includes runnable code, smoke-test that code. +- For code changes, run focused tests first, then broader tests when the blast radius justifies it. + +## Knowledge Captured + +- 2026-05-06: Elume's public story is library-first. Dionysus Core consuming `elume` is evidence that Elume works as a downstream dependency, but it is not the main onboarding story. The main onboarding story should show a clean user building with Elume directly from PyPI. +- 2026-05-06: The quickstart should demonstrate Elume's deterministic memory/replay value without requiring Dionysus. Dionysus belongs in a later "integration example" section. +- 2026-05-06: Beginner-facing quickstart copy should not name unrelated adjacent libraries or frameworks. The reader is agnostic and should only see the tools needed for the tutorial. +- 2026-05-06: `linoss-dynamics` is its own domain-agnostic numerical runtime. Elume may delegate raw oscillator stepping to it, but Elume owns `LinOSSEncoder`, trajectory records, belief/basin meaning, and replay/memory semantics. Do not push Elume-specific encoders into `linoss-dynamics`. + +## Graphify + +This project has a Graphify knowledge graph at `graphify-out/`. + +Rules: +- Before answering architecture or codebase questions, read `graphify-out/GRAPH_REPORT.md` for god nodes and community structure. +- If `graphify-out/wiki/index.md` exists, navigate it instead of reading raw files. +- For cross-module "how does X relate to Y" questions, prefer `graphify query ""`, `graphify path "" ""`, or `graphify explain ""` over grep. These traverse the graph's extracted and inferred edges instead of scanning files. +- After modifying code files in this session, run `graphify update .` to keep the graph current. + +## Graphify Boundary + +Graphify is a project map, not ambient source context. + +- Do not read or re-index `graphify-out/` during normal agent work. +- Do not treat Graphify output as campaign or source truth unless a run manifest explicitly authorizes it. +- Use `.graphifyignore` before running Graphify. +- Prefer `graphify update .` over full rebuilds. +- Historical graph snapshots belong under `conductor/archive/graphs/` only at meaningful milestones, and should be linked from track closure notes rather than pasted into active tracks. +- Do not index archives, generated runs, worktrees, Sync directories, secrets, or dependency/build artifacts. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/GEMINI.md b/GEMINI.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/README.md b/README.md index 8056335..de3c495 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,13 @@ Requires Python `>=3.11`. pip install elume ``` +## Quickstart + +Start with the standalone tutorial: [docs/quickstart.md](./docs/quickstart.md). + +It shows Elume as an independent Python library. You only need Python and the +`elume` package. + ## Quickstart (development) For local development, use [`uv`](https://github.com/astral-sh/uv) and an editable install: diff --git a/docs/quickstart.md b/docs/quickstart.md new file mode 100644 index 0000000..2fe4d89 --- /dev/null +++ b/docs/quickstart.md @@ -0,0 +1,122 @@ +# Quickstart: Run A Deterministic Memory Step With Elume + +This tutorial shows Elume as a standalone Python library. You only need Python +and the `elume` package. + +By the end, you will run one replay-safe memory evolution step and verify that +the same input produces the same output hash twice. + +## What Elume Does + +Elume helps an AI agent remember what happened, compare old choices, and try a +better memory strategy next time. It is different because it is built to be +repeatable: if you give it the same starting memory and the same seed, it gives +you the same result again. That makes it useful when you want agent memory you +can test, audit, replay, and plug into your own app without adopting a larger +system. + +## 1. Install + +Create a small project folder and install Elume from PyPI: + +```bash +python -m venv .venv +source .venv/bin/activate +pip install elume==0.4.0 +``` + +## 2. Create The Example + +Create `elume_quickstart.py`: + +```python +from elume.envelope.ops import resolve +from elume.envelope.protocol import SCHEMA_VERSION, EnvelopeInput, Verdict +from elume.envelope.snapshot import serialize_strategies +from elume.models import Strategy + + +strategies = ( + Strategy(name="careful", genotype={"temperature": 0.2}, created_at=1.0), + Strategy(name="balanced", genotype={"temperature": 0.6}, created_at=1.0), + Strategy(name="bold", genotype={"temperature": 0.9}, created_at=1.0), +) + +scenario = EnvelopeInput( + schema_version=SCHEMA_VERSION, + scenario_id="quickstart.evolution_step", + run_id="run-1", + seed=123, + operation="evolution.step", + operation_args={ + "fitness": {"careful": 0.2, "balanced": 0.8, "bold": 0.5}, + "elite_k": 1, + "operator_config": {"kind": "jitter", "scale": 0.05}, + }, + provider_snapshot=serialize_strategies(strategies), +) + +operation = resolve("evolution.step") +first = operation.run(scenario) +second = operation.run(scenario) + +assert first.verdict is Verdict.PASS +assert first.result == second.result +assert first.post_state_hash == second.post_state_hash + +children = list(first.result["children"]) +print("verdict:", first.verdict) +print("new children:", [child["name"] for child in children]) +print("population:", first.result["population_ids"]) +print("post-state hash:", first.post_state_hash) +print("replay matched:", first.post_state_hash == second.post_state_hash) +``` + +## 3. Run It + +```bash +python elume_quickstart.py +``` + +You should see: + +```text +verdict: PASS +new children: [...] +population: [...] +post-state hash: ... +replay matched: True +``` + +The exact child names and hash are implementation details. The important part +is `replay matched: True`: the same scenario produced the same result twice. + +## What You Built + +You gave Elume three memory strategies: + +- `careful`, with a low temperature. +- `balanced`, with a middle temperature. +- `bold`, with a high temperature. + +Then you scored them, preserved the best one, and let Elume create new child +strategies from the current population. Elume wrapped the step in its +deterministic envelope, so the output includes a replay hash that can be checked +later. + +## Why This Matters + +Most agent memory systems are hard to inspect once they start changing +themselves. Elume makes the changing part explicit: the strategies are typed, +the provider boundary is clean, and the replay envelope gives each operation a +stable audit trail. + +That means you can use Elume as the memory kernel inside your own agent, +research notebook, service, benchmark, or application. + +## Next Steps + +- Use `elume.providers.InMemoryProvider` as the reference provider contract. +- Explore `elume.envelope.ops` for replay-safe operations. +- Use `elume.basins`, `elume.linoss`, and `elume.embedders` when you need + belief retrieval and trajectory encoding. From a64da6e5136a065d4b04c8212f9b06d1118a4744 Mon Sep 17 00:00:00 2001 From: "Mani Saint-Victor, MD" Date: Wed, 6 May 2026 16:45:18 -0400 Subject: [PATCH 2/5] chore: add graphify repository boundaries --- .graphifyignore | 52 ++++++++++++++++++++++++++++++ conductor/archive/graphs/README.md | 20 ++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 .graphifyignore create mode 100644 conductor/archive/graphs/README.md diff --git a/.graphifyignore b/.graphifyignore new file mode 100644 index 0000000..31d0834 --- /dev/null +++ b/.graphifyignore @@ -0,0 +1,52 @@ +# Graphify input exclusions +# Purpose: keep Graphify as an intentional map, not a recursive/bloated corpus. + +# Graphify's own outputs and transient files +graphify-out/ +.graphify_* +*.graphml +cypher.txt + +# Archives and historical snapshots: query intentionally, do not re-index by default +archive/ +archives/ +.archives/ +conductor/archive/ +conductor/archives/ +.archive/ + +# Agent/workflow generated state and logs +.archon/run/ +.worktrees/ +.worktree/ +logs/ +*.log +*.tmp +*.bak + +# Dependencies/build artifacts +.git/ +node_modules/ +.venv/ +venv/ +__pycache__/ +dist/ +build/ +.next/ +.cache/ +.pytest_cache/ +.coverage + +# Sensitive/local configuration +.env +.env.* +*.pem +*.key +*.sqlite +*.sqlite3 + +# Protected sync folders: require explicit per-request approval +Sync/ +sync/ +**/Sync/** +**/sync/** diff --git a/conductor/archive/graphs/README.md b/conductor/archive/graphs/README.md new file mode 100644 index 0000000..8684e54 --- /dev/null +++ b/conductor/archive/graphs/README.md @@ -0,0 +1,20 @@ +# Graphify Snapshot Archive + +Store only selected historical Graphify snapshots here. + +Use this archive for milestones with historical value, such as: + +- major architecture transitions +- before/after migrations +- Conductor track closure points +- release or recovery milestones + +Recommended snapshot contents: + +- `GRAPH_REPORT.md` +- `graph.json` +- `graph.html` only when visual inspection matters + +Do not archive every run. Keep the active current map in the project root `graphify-out/` and use `graphify update .` for routine refreshes. + +Agents should not read this archive unless a task or run manifest explicitly authorizes that snapshot. From ab11ce37499b3020940470f8d062a5d8b39ddac2 Mon Sep 17 00:00:00 2001 From: "Mani Saint-Victor, MD" Date: Wed, 6 May 2026 16:50:48 -0400 Subject: [PATCH 3/5] docs: record Elume session reflection --- AGENTS.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 3daee3c..8072f9e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,12 +20,21 @@ This is the canonical instruction file for agents working in this repository. - For docs-only changes, run at least `git diff --check`; when a tutorial includes runnable code, smoke-test that code. - For code changes, run focused tests first, then broader tests when the blast radius justifies it. +## Reflection Practice + +- ALWAYS do some reflection after a correction from Dr. Mani changes the frame of the work. +- Reflect about the reflection honestly: identify whether the mistake was a fact error, a framing error, an audience error, or an execution error. +- If the mistake is durable and likely to recur, add the lesson to this file in plain language before wrapping the session. +- Keep reflection practical. The goal is better next-session behavior, not apology text. + ## Knowledge Captured - 2026-05-06: Elume's public story is library-first. Dionysus Core consuming `elume` is evidence that Elume works as a downstream dependency, but it is not the main onboarding story. The main onboarding story should show a clean user building with Elume directly from PyPI. - 2026-05-06: The quickstart should demonstrate Elume's deterministic memory/replay value without requiring Dionysus. Dionysus belongs in a later "integration example" section. - 2026-05-06: Beginner-facing quickstart copy should not name unrelated adjacent libraries or frameworks. The reader is agnostic and should only see the tools needed for the tutorial. - 2026-05-06: `linoss-dynamics` is its own domain-agnostic numerical runtime. Elume may delegate raw oscillator stepping to it, but Elume owns `LinOSSEncoder`, trajectory records, belief/basin meaning, and replay/memory semantics. Do not push Elume-specific encoders into `linoss-dynamics`. +- 2026-05-06: Today's main miss was a framing error, not a lack of code context. I kept pulling explanations toward Dionysus, MemEvolve, and LinOSS because those were salient from recent work. Dr. Mani's correction was that beginner-facing Elume material should start from the user's task and only introduce names the user needs right now. +- 2026-05-06: Reflection on that reflection: the first reflection can still be too self-centered if it only says "I over-mentioned adjacent systems." The operational rule is sharper: before writing a quickstart, ask what the reader is trying to do in the first five minutes, then remove every proper noun that does not help that action. ## Graphify From 0a9a8acf842c07fa37fb5c4a0a1297170cfe5d57 Mon Sep 17 00:00:00 2001 From: "Mani Saint-Victor, MD" Date: Wed, 6 May 2026 22:40:11 -0400 Subject: [PATCH 4/5] docs: inline quickstart value example --- README.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index de3c495..6b7ba29 100644 --- a/README.md +++ b/README.md @@ -160,10 +160,66 @@ pip install elume ## Quickstart -Start with the standalone tutorial: [docs/quickstart.md](./docs/quickstart.md). +This example shows Elume's core value in one small script: a memory strategy can +change, and the change is still replayable. The same input produces the same +result and the same post-state hash, which gives you an audit trail for agent +memory instead of an opaque update. + +Create `elume_quickstart.py`: + +```python +from elume.envelope.ops import resolve +from elume.envelope.protocol import SCHEMA_VERSION, EnvelopeInput, Verdict +from elume.envelope.snapshot import serialize_strategies +from elume.models import Strategy + + +strategies = ( + Strategy(name="careful", genotype={"temperature": 0.2}, created_at=1.0), + Strategy(name="balanced", genotype={"temperature": 0.6}, created_at=1.0), + Strategy(name="bold", genotype={"temperature": 0.9}, created_at=1.0), +) + +scenario = EnvelopeInput( + schema_version=SCHEMA_VERSION, + scenario_id="quickstart.evolution_step", + run_id="run-1", + seed=123, + operation="evolution.step", + operation_args={ + "fitness": {"careful": 0.2, "balanced": 0.8, "bold": 0.5}, + "elite_k": 1, + "operator_config": {"kind": "jitter", "scale": 0.05}, + }, + provider_snapshot=serialize_strategies(strategies), +) + +operation = resolve("evolution.step") +first = operation.run(scenario) +second = operation.run(scenario) + +assert first.verdict is Verdict.PASS +assert first.result == second.result +assert first.post_state_hash == second.post_state_hash + +children = list(first.result["children"]) +print("verdict:", first.verdict) +print("new children:", [child["name"] for child in children]) +print("population:", first.result["population_ids"]) +print("post-state hash:", first.post_state_hash) +print("replay matched:", first.post_state_hash == second.post_state_hash) +``` + +Run it: + +```bash +python elume_quickstart.py +``` + +The important line is `replay matched: True`. Elume created new child memory +strategies, then proved that the same scenario can be replayed exactly. -It shows Elume as an independent Python library. You only need Python and the -`elume` package. +For a fuller walkthrough, see [docs/quickstart.md](./docs/quickstart.md). ## Quickstart (development) From 86103eb3197c980d59cb053e3b0f538a9e96a32b Mon Sep 17 00:00:00 2001 From: "Mani Saint-Victor, MD" Date: Sun, 10 May 2026 01:48:52 -0400 Subject: [PATCH 5/5] docs: add Elume/MemEvolve/LinOSS genealogy walkthrough --- docs/elume-memevolve-genealogy.md | 746 ++++++++++++++++++++++++++++++ 1 file changed, 746 insertions(+) create mode 100644 docs/elume-memevolve-genealogy.md diff --git a/docs/elume-memevolve-genealogy.md b/docs/elume-memevolve-genealogy.md new file mode 100644 index 0000000..67f4235 --- /dev/null +++ b/docs/elume-memevolve-genealogy.md @@ -0,0 +1,746 @@ +# Elume, MemEvolve, and LinOSS — A Genealogy and Architecture Walkthrough + +This document explains, from first principles, what MemEvolve is, what we built +under the misleading name `memevolve_*` in Dionysus 3 (and why none of it is +actually MemEvolve), how that work became Elume, and what LinOSS does inside +the Elume substrate. + +It is written so a reader who has never seen any of this can follow it end to +end. Code citations point to the actual source on disk so you can verify any +claim by opening the file. + +--- + +## Part 1 — A naive user's guide to MemEvolve + +### 1.1 The problem MemEvolve solves + +Most "agentic memory" research evolves the *contents* of a memory store — what +gets remembered, what gets forgotten, what gets reinforced — while keeping the +**architecture** of the store fixed. The hash-chained vector buffer stays a +hash-chained vector buffer; the knowledge graph stays a knowledge graph. Only +the data inside changes. + +MemEvolve (Zhang et al. 2025, arXiv:2512.18746, ICML 2026) attacks the prior +question: *what if the architecture itself can be evolved?* They formalize a +**dual-evolution process** that jointly evolves: + +- **Memory base** *Mt* — what the agent remembers at time *t*. +- **Memory architecture** *Ω* — the interface and operations the agent uses to + read/write/maintain that memory. + +The README states this directly: + +> Our framework, MemEvolve, enables both the memory content and the memory +> system itself to adapt and improve over time through meta-evolution. + +The "meta" is the second loop — searching over architectures, not just +populating one architecture. + +### 1.2 EvolveLab — the unified codebase + +The repo at `github.com/bingreeky/MemEvolve` ships under the umbrella name +**EvolveLab**, "a Unified Codebase for Self-Evolving Memory" (per the upstream +README). EvolveLab is the harness: a common `BaseMemoryProvider` interface, +14 reproduced memory baselines, three benchmarks (GAIA, WebWalkerQA, xBench), +and a meta-evolution loop on top. + +When you read `Flash-Searcher-main/EvolveLab/` you see: + +``` +base_memory.py # the abstract BaseMemoryProvider +memory_types.py # MemoryRequest, MemoryResponse, MemoryType, etc. +config.py # provider config loader +providers/ # 14 concrete providers (one file each) +schema/, services/, tests/ +``` + +The point of EvolveLab is that **every memory architecture exposes the same +three-method interface**, so the meta-evolution loop can swap one architecture +for another without changing the agent. + +### 1.3 The four memory operations (E / U / R / G) + +The MemEvolve paper formalizes any agent memory system as a tuple of four +operations. This is the conceptual decomposition the paper calls the +**genotype**: + +| Symbol | Name | Plain English | When it runs | +|--------|-----------|--------------------------------------------------------------|------------------------------------| +| **E** | Encode | Turn the incoming experience into a stored representation. | Online, on every step. | +| **U** | Store | Decide what to write, where to write it, and what to index. | Online, on every write. | +| **R** | Retrieve | Given a query, return ranked memory items. | Online, on every retrieval. | +| **G** | Manage | Offline maintenance: consolidate, prune, forget, evolve. | Offline, between rollouts. | + +In code, the boundary is drawn through `BaseMemoryProvider` +(`Flash-Searcher-main/EvolveLab/base_memory.py:10`). The interface exposes +exactly three abstract methods — E and U are folded into one call, R is its +own call, and G is what the *outer* meta-loop does between runs: + +```python +class BaseMemoryProvider(ABC): + def initialize(self) -> bool: ... + def provide_memory(self, request: MemoryRequest) -> MemoryResponse: ... # R + def take_in_memory(self, td: TrajectoryData) -> tuple[bool, str]: ... # E + U +``` + +**`provide_memory`** is the R-stage call. The agent passes a `MemoryRequest` +(query + context + status) and gets back a ranked `MemoryResponse`. + +**`take_in_memory`** is the combined E + U call. The agent hands over a +`TrajectoryData` (the run so far), and the provider encodes it (E) and stores +it (U). Returns `(success, description)`. + +**G** does not appear as a method on the provider. G is what the meta-loop +(Part 1.5) does *between* benchmark rollouts: analyzing failures, mutating +architectures, replacing one provider with a successor. + +### 1.4 The inner loop — one rollout with one architecture + +The inner loop is **a single agent run on a single benchmark task with a +single memory architecture plugged in**. Concretely (from +`run_flash_searcher_mm_gaia.py`): + +``` +1. Resolve the chosen provider via PROVIDER_MAPPING[memory_type]. +2. provider.initialize(). +3. For each step in the agent rollout: + a. Build MemoryRequest(query, context, status=BEGIN | IN). + b. provider.provide_memory(request) → ranked memories. (R) + c. Agent reasons, picks a tool, acts, observes. + d. provider.take_in_memory(trajectory_so_far) (E + U) +4. Score the final answer against ground truth (GAIA / WebWalker / xBench). +5. Emit JSON: per-step retrieval log + final accuracy. +``` + +The `MemoryStatus` enum has only two values +(`memory_types.py:11-15`): + +- `BEGIN` — task entry. Before any actions are taken. Used to retrieve + long-horizon strategic and procedural priors. +- `IN` — mid-task. After at least one action. Used to retrieve episodic and + contextual memories from the run so far. + +This `BEGIN` / `IN` distinction is **phase-aware retrieval** — different memory +*types* are surfaced depending on where the agent is in the task, not just +similarity to the query. + +### 1.5 The outer loop — meta-evolution across rounds + +The outer loop is implemented by `MemoryEvolver` +(`MemEvolve/core/memory_evolver.py`) and orchestrated across rounds by +`AutoEvolver` (`MemEvolve/core/auto_evolver.py:38`). Its state machine has +**four phases**: + +``` +self.state["phases"] = { + "analyze": {"completed": False}, + "generate": {"completed": False}, + "create": {"completed": False}, + "validate": {"completed": False}, +} +``` + +These are the **four phases of MemEvolve**. Each one is implemented by its +own module under `MemEvolve/phases/`: + +| Phase | Module | What it does | +|-------|--------------------------------------|------------------------------------------------------------------------------------------------------------------| +| **1. Analyze** | `phases/phase_analyzer.py` | Read the trajectories from the previous round of inner-loop runs. Identify failure modes, common mistakes, missed retrievals. Produce an analysis report. | +| **2. Generate** | `phases/phase_generator.py` | Use an LLM (`ANALYSIS_MODEL` / `GENERATION_MODEL` — defaults `gpt-5`, recommended `claude-sonnet-4.5`) to propose a new memory system. The proposal is a config: class name, module name, enum value, config params. | +| **3. Create** | `phases/memory_creator.py` | Materialize the new memory system on disk: write the new provider Python file, update `memory_types.py` to register it. Now it is callable. | +| **4. Validate** | `phases/phase_validator.py` | Run static checks + 5 sanity tests against the new system. If it fails, mark for regeneration. If it passes, it is eligible to enter the next round's tournament. | + +`AutoEvolver` runs this for `num_rounds` rounds, each round drawing +`task_batch_x` tasks for the inner loop, surviving `top_t` top providers, +generating `num_systems` (= `EVOLVE_GENERATED_M`) new candidates per round. + +### 1.6 Flash Searcher — the inner-loop runner + +**Flash Searcher** is the agent harness. It is the script at +`Flash-Searcher-main/run_flash_searcher_mm_gaia.py` (and the matching +`*_xbench.py` and `*_webwalkerqa.py`). It is what *runs* the inner loop on a +benchmark, given a chosen memory provider: + +```bash +python run_flash_searcher_mm_gaia.py \ + --infile ./data/gaia/validation/metadata.jsonl \ + --outfile ./gaia_output/lightweight_results.jsonl \ + --memory_provider lightweight_memory \ + --sample_num 20 \ + --max_steps 40 +``` + +You can read the import line that wires it to EvolveLab +(`run_flash_searcher_mm_gaia.py:30`): + +```python +from EvolveLab.memory_types import MemoryType, TrajectoryData, PROVIDER_MAPPING +from EvolveLab.config import get_memory_config +``` + +Flash Searcher's job is purely the inner loop — the meta-evolution lives in +`MemEvolve/`, the agent harness lives in `Flash-Searcher-main/`. They meet +through `BaseMemoryProvider`. + +### 1.7 Putting it all together — one picture + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ OUTER LOOP — MemEvolve meta-evolution │ +│ (MemEvolve/core/auto_evolver.py) │ +│ │ +│ Phase 1 ─► Phase 2 ─► Phase 3 ─► Phase 4 ─► next round │ +│ Analyze Generate Create Validate │ +│ ▲ (LLM) (write (5 sanity │ +│ │ .py) tests) │ +│ │ │ +│ └─────── trajectories from inner loop ◄──────────┐ │ +└────────────────────────────┬──────────────────────────┘ │ + │ │ + │ one provider per inner-loop run │ + ▼ │ +┌──────────────────────────────────────────────────────────────────┐│ +│ INNER LOOP — Flash Searcher ││ +│ (Flash-Searcher-main/run_flash_searcher_*.py) ││ +│ ││ +│ for step in agent rollout: ││ +│ MemoryRequest ─► provide_memory (R) ││ +│ agent reasons, acts, observes ││ +│ trajectory ─► take_in_memory (E + U) ││ +│ ││ +│ end-of-task: outer loop runs G ──────────────────────────────┘│ +└──────────────────────────────────────────────────────────────────┘ + │ + │ inside the chosen provider + ▼ +┌──────────────────────────────────────────────────────────────────┐ +│ ARCHITECTURE Ω = (E, U, R, G) │ +│ │ +│ Whichever provider got picked: AGENT_KB, EXPEL, DILU, MEMP, │ +│ GENERATIVE, EVOLVER, DIONYSUS, … or a Phase-2-synthesized │ +│ newcomer. Same interface; different internals. │ +└──────────────────────────────────────────────────────────────────┘ +``` + +That is MemEvolve. Two loops, one interface, four phases in the outer loop, +four operations (E / U / R / G) in every architecture, with Flash Searcher as +the inner-loop runner. + +--- + +## Part 2 — What is in `dionysus3/api/.../memevolve_*` is **not** MemEvolve + +This part exists because the file names lie about lineage. + +### 2.1 The naming fossil + +Inside Dionysus 3 there is a constellation of files that look, at first glance, +like a MemEvolve port: + +``` +dionysus3/api/models/memevolve.py +dionysus3/api/services/memevolve_provider.py +dionysus3/api/services/memevolve_diagnostician.py +dionysus3/api/services/memevolve_designer.py +dionysus3/api/services/memevolve_validator.py +dionysus3/api/services/memevolve_auto_evolver.py +dionysus3/api/services/memevolve_evolution_engine.py +dionysus3/api/services/memevolve_feedback.py +dionysus3/api/services/memevolve_pipeline/ +dionysus3/api/routers/memevolve.py +dionysus3/n8n-workflows/memevolve-evolve.json +``` + +The file names *say* "MemEvolve." The contents are not. They are **proto-Elume** +— the substrate, encoder, evolution engine, curiosity homing, basin enrichment, +and trajectory feedback that eventually became `elume.linoss`, +`elume.basins`, `elume.cognition.curiosity`, `elume.evolution`, and +`elume.adapters.memevolve` — wearing a borrowed name from a period before we +knew the upstream paper existed. + +The naming is a fossil from when we believed we were *implementing* MemEvolve. +We were not. We were building toward a similar abstraction (E / U / R / G, +mutation, tournament selection) and converged on a similar shape because we had +read the paper, but the **code under those filenames is ours**, not a port. + +### 2.2 How the discovery happened (Wave 0 / Track 597) + +The dossier at +`dionysus3/docs/oss-extraction/memevolve-upstream.md` (verified 2026-04-24) +caught the upstream identity and reached an unambiguous verdict: + +> **MemEvolve is upstream OSS. Not a Dionysus invention.** Same playbook as +> Nemori applies: contribute deltas upstream; do NOT ship +> `memevolve-enhancement-kit` as a Dionysus-owned package. + +The convergent evidence cited: + +- `ArchitectureGenotype` at `api/models/memevolve.py:217-229` had fields + named `encode / store / retrieve / manage` — the paper's E / U / R / G. +- An explicit code comment said *"EvolveLab-compatible"* — `EvolveLab` is the + paper's name for the implementation. +- `BaseMemoryProvider` was reinvented in `api/services/memevolve_provider.py` + with the comment *"Provider interface parity with paper-style memory + providers."* +- `AutoEvolveRequest` parameters `kmax`, `survivors_k`, `descendants_s`, + `creativity_index`, `seed_architectures` are the paper's tournament-style + hyperparameters — not invented by us. +- `ManageOperation` enum values `EVOLVE / CONSOLIDATE / PRUNE / FORGET` are the + paper's described offline G-stage operations. + +The decision tree changed from "ship as our package" to "upstream-contribute, +with an optional thin bridge package." The proto-Elume code remained where it +was, under its old name, because renaming a live FastAPI surface mid-flight is +risky. But the *meaning* changed: every file under `memevolve_*` is now +understood as *Elume work that was filed under the wrong name*. + +### 2.3 Element-by-element: what we actually built + +For each file/module under the `memevolve_*` umbrella in Dionysus 3, this is +what it does, how it ties back to MemEvolve (paper or code), and why we did +not just `import` from MemEvolve. + +#### `api/models/memevolve.py` — domain models + +- **What it does.** Defines Pydantic models for the meta-evolution surface: + `ArchitectureGenotype` (with `encode`, `store`, `retrieve`, `manage` + fields), `AutoEvolveRequest` (tournament hyperparams), + `ManageOperation` enum (EVOLVE / CONSOLIDATE / PRUNE / FORGET). +- **Tie-back to MemEvolve.** The *shape* of the genotype is paper-derived. + E/U/R/G is the paper's formalization. The G-stage operations match the + paper's described offline maintenance set. +- **Why not import.** MemEvolve's `memory_types.py` does not even define an + `ArchitectureGenotype` — the paper's genotype is a *conceptual* tuple, not a + concrete Pydantic class. We needed a typed FastAPI request/response surface, + bound to our event bus and HMAC webhook plane. Importing would have given us + `MemoryRequest` / `MemoryResponse` / `TrajectoryData` (which we *do* duck-type + against in the cartridge), but no genotype model — so there was nothing to + import for this file. +- **Where it lives now in Elume.** `elume/models/strategy.py` (`Strategy` — + frozen dataclass, immutable successors) and the parameter set on + `EvolutionEngine` (`elume/evolution/engine.py`). + +#### `api/services/memevolve_provider.py` — provider abstraction + +- **What it does.** Defines our own abstract `BaseMemoryProvider` plus + in-process subclasses, with the comment *"Provider interface parity with + paper-style memory providers."* +- **Tie-back to MemEvolve.** Mirrors the paper's three-method interface + (`initialize`, `provide_memory`, `take_in_memory`). +- **Why not import.** MemEvolve installs its providers as Python modules + loaded by `PROVIDER_MAPPING`'s `(class_name, module_name)` resolution. To + import `BaseMemoryProvider` directly from MemEvolve we would have had to + `pip install` MemEvolve into Dionysus's runtime — pulling in its LLM clients + (model_service, ~860 LOC), session manager (~590 LOC), Neo4j service + (~830 LOC), and benchmark data loaders. That is a research stack, not a + service runtime. We took the *interface shape* and re-declared it in our + own dependency-light surface. +- **Where it lives now in Elume.** `elume/adapters/memevolve/provider.py:70` + `ElumeMemoryProvider` — duck-typed against `BaseMemoryProvider` so the elume + package stays free of any MemEvolve runtime import. + +#### `api/services/memevolve_evolution_engine.py` — the engine + +- **What it does.** Drives a population of strategies through generations of + mutation and selection. Reads, mutates, scores, replaces. +- **Tie-back to MemEvolve.** Conceptually the same role as MemEvolve's + `MemoryEvolver` (`MemEvolve/core/memory_evolver.py`) — but MemEvolve's + evolver is built around an LLM-driven `analyze → generate → create → + validate` state machine that *writes new Python files to disk*. Ours is a + numeric / data-flow engine over frozen `Strategy` objects. +- **Why not import.** Two reasons. First, MemEvolve's evolver writes new + provider source files via an LLM — we did not want LLM-as-codegen as the + inner mechanism of our service-layer evolution; we wanted deterministic + parameter mutation over a typed `Strategy`. Second, MemEvolve mutates + in-place; our invariant is that successors are new objects via + `Strategy.evolved()` (so causal lineage is preserved and replays are + reproducible). The two engines are doing similar evolutionary work over + different *units* of selection. +- **Where it lives now in Elume.** `elume/evolution/engine.py` + `EvolutionEngine` (deterministic, frozen `Strategy`, + provider-as-persistence-boundary). + +#### `api/services/memevolve_auto_evolver.py` — orchestration wrapper + +- **What it does.** Multi-round orchestrator: registry + harness + + diagnostician + designer + feedback fusion across rounds. +- **Tie-back to MemEvolve.** Plays the role of MemEvolve's `AutoEvolver` + (`MemEvolve/core/auto_evolver.py:38`) — multi-round loop, dataset cursor, + best-provider tracking. +- **Why not import.** MemEvolve's `AutoEvolver` calls into its own LLM-driven + phase machinery. Ours wires our diagnostician/designer/validator/feedback + components together, none of which exist in MemEvolve. +- **Where it lives now in Elume.** `elume/evolution/auto_evolver.py` + `AutoStrategyEvolver` — slimmed to the deterministic-substrate surface, + with plateau detection. + +#### `api/services/memevolve_diagnostician.py` — failure analysis + +- **What it does.** Reads trajectories from the previous round, identifies + failure modes (wrong retrieval, missed phase, etc.). +- **Tie-back to MemEvolve.** The paper's Phase 1 (Analyze) plays a similar + role. +- **Why not import.** MemEvolve's `PhaseAnalyzer` reads trajectories produced + by Flash Searcher in a specific JSONL shape and produces a markdown report + for an LLM to consume in Phase 2. Ours produces structured failure-mode + records consumed by our designer service over the event bus, not markdown + for an LLM. Different output, different consumer. +- **Where it lives now.** Not extracted — application-layer, kept in + Dionysus 3. + +#### `api/services/memevolve_designer.py` — proposal synthesis + +- **What it does.** LLM-driven proposal of E/U/R/G mutations. +- **Tie-back to MemEvolve.** The paper's Phase 2 (Generate) is the closest + match — also LLM-driven proposal of new providers. +- **Why not import.** MemEvolve's `PhaseGenerator` proposes a *full new + provider class* and emits a config that Phase 3 turns into a brand-new + `.py` file. Our designer proposes genotype mutations within an existing + parameter space — different unit of evolution, different output. +- **Where it lives now.** Not extracted — application-layer. + +#### `api/services/memevolve_validator.py` — validation + +- **What it does.** Static checks + smoke tests against a proposed + architecture. +- **Tie-back to MemEvolve.** Phase 4 (Validate) does the same job upstream, + with 5 sanity tests. +- **Why not import.** MemEvolve's validator exec's the newly-written provider + module and runs a fixed test battery. Ours validates that mutations are + well-formed within our typed `Strategy` schema — different artifact, + different checks. +- **Where it lives now.** Not extracted. + +#### `api/services/memevolve_feedback.py` — 12-dimensional trajectory feedback + +- **What it does.** Computes a 12-dimensional feedback vector from a + trajectory: success, latency, retrieval-precision, basin-coverage, etc. +- **Tie-back to MemEvolve.** **None.** This is a real Dionysus contribution. + The dossier flags it as an extension on top of upstream. MemEvolve scores + trajectories with single-number accuracy on benchmark tasks; our feedback + is a multi-dimensional signal that drives the inner-loop curiosity homing + device and feeds the outer-loop diagnostician. +- **Where it lives now.** Application-layer; the curiosity-homing reduction + of it is in `elume/cognition/curiosity.py` and the curiosity rerank in + `elume/adapters/memevolve/provider.py:391`. + +#### `api/services/memevolve_pipeline/` — pipelined E→U→R→G runner + +- **What it does.** Component/registry/executor for chaining the four + operations as a configurable pipeline. Includes integration steps like + `graphiti_search` and `learning_signal_capture`. +- **Tie-back to MemEvolve.** The conceptual chain is the paper's. The + *concrete* steps (Graphiti search, learning-signal capture) are ours. +- **Why not import.** Upstream MemEvolve has no Graphiti client (it has its + own `graphiti_client.py`, removed in upstream cleanup); upstream has no + learning-signal capture step. Our pipeline is bespoke to our knowledge-graph + backend. +- **Where it lives now.** Application-layer; the deterministic pieces of + E (encoding) live in `elume/adapters/memevolve/encode.py` and + `elume/linoss/encoder.py`. + +#### `api/routers/memevolve.py` — FastAPI HTTP surface + +- **What it does.** Exposes the whole evolution surface as REST + webhooks. +- **Tie-back to MemEvolve.** None (MemEvolve is a CLI/research codebase, no + HTTP API). +- **Why not import.** Nothing to import. +- **Where it lives now.** Application-layer. + +#### LinOSS belief-state injection — the encode-stage delta + +- **What it does.** Before our provider hashes a query into a Hopfield + pattern, it builds a deterministic belief prior from the query, runs that + belief through a `LinOSSEncoder`, re-discretizes the hidden state to ±1, and + blends 50/50 with the SHA-256 base pattern. The result is a Hopfield key + that already carries oscillatory belief structure — not just query bytes. +- **Tie-back to MemEvolve.** Lives inside the **E** stage of our provider. + Replaces a vanilla "embed and hash" encode with an oscillator-augmented + one. The shape of E is the paper's; the math inside E is ours. +- **Why not import.** Upstream has no LinOSS dependency, no belief embedder, + and the original dionysus3 encoder had a real bug (see Part 4) that we + needed to fix at the kernel level rather than mod the upstream provider. +- **Where it lives now.** `elume/linoss/encoder.py` (encoder), + `elume/linoss/solver.py` (the math), `elume/embedders/belief_embedder.py`, + and the blend in `elume/adapters/memevolve/encode.py:90-135`. + +#### `n8n-workflows/memevolve-evolve.json` — operations workflow + +- **What it does.** Triggers and monitors evolution runs from n8n. +- **Tie-back to MemEvolve.** None. +- **Why not import.** Nothing to import. +- **Where it lives now.** Application-layer. + +### 2.4 Why we built our own surface instead of importing MemEvolve + +There are six distinct reasons, each load-bearing: + +1. **Determinism is a substrate-level invariant.** MemEvolve's reference + providers use module-global RNG, wall-clock session IDs, and sort orders + that are not stable across Python implementations. Elume's invariant is + *byte-identical replay across a `platform_fingerprint()`*. You cannot + bolt determinism onto a provider whose internals were not built with it + in mind — we had to write providers from the inside out with seeded + per-instance RNG. + +2. **Runtime weight.** Importing MemEvolve pulls in LLM clients + (`model_service.py`, ~860 LOC), session/Neo4j services (~1400 LOC + combined), Graphiti client, and benchmark data loaders. Elume is meant to + be a slim Python library shipped on PyPI: `pip install elume` should not + pull a research stack. + +3. **Inversion of dependency.** When a memory architecture *plugs into* + MemEvolve as a baseline, the dependency direction is **MemEvolve → + Elume**, not the other way around. The cartridge file + (`elume/adapters/memevolve/provider.py:7-13`) makes this explicit: it + does *not* import any MemEvolve type at runtime; it duck-types against + the interface and returns dicts shaped for `MemoryResponse(**d)` + construction by a one-file MemEvolve-side shim. + +4. **License and attribution clarity.** MemEvolve is Apache-2.0. Where we + ported actual code (PII regex, content extraction in + `elume/adapters/memevolve/shaping.py`), the file header and inline + comments cite the source line ranges. Where we re-implemented the + *interface shape*, that is a paper-idea derivation, not a code port. We + keep the boundary clean by not entangling import graphs. + +5. **Different unit of evolution.** MemEvolve evolves *Python source files* + — Phase 3 literally writes new `.py` files. Our `EvolutionEngine` evolves + *parameter values* on a frozen `Strategy` schema. Importing MemEvolve's + `MemoryEvolver` would force us to evolve at MemEvolve's granularity (full + provider classes), which defeats the immutability invariants that make + Elume replayable. + +6. **Pre-release upstream.** MemEvolve had no tagged releases; the + `BaseMemoryProvider` interface is pinned at commit `6f9c0a2` in our + adapter docs. Pinning a pre-release upstream into a production runtime is + a maintenance trap. Treating it as a *target* we plug *into* — not a + dependency we run on — keeps the integration boundary thin. + +--- + +## Part 3 — Elume as the deterministic substrate + +### 3.1 The cartridge + +`elume.adapters.memevolve` is the cartridge that lets Elume show up inside +MemEvolve's `PROVIDER_MAPPING` as one more baseline. Two changes on the +MemEvolve side and one shim file are all that's needed +(`docs/adapters/memevolve.md:49-89`): + +```python +# 1) memory_types.py — add to MemoryType enum: +class MemoryType(str, Enum): + ... + ELUME = "elume" + +# 2) memory_types.py — add to PROVIDER_MAPPING: +PROVIDER_MAPPING[MemoryType.ELUME] = ("ElumeMemoryProvider", "elume_memory_provider") + +# 3) EvolveLab/providers/elume_memory_provider.py: +from elume.adapters.memevolve import ElumeMemoryProvider +__all__ = ["ElumeMemoryProvider"] +``` + +That is the entire integration. The Flash Searcher loader will find +`ElumeMemoryProvider` via `PROVIDER_MAPPING`, call `initialize()`, and run the +inner loop against it like any other baseline. + +### 3.2 How Elume realizes E / U / R / G + +| MemEvolve stage | Elume primitive | File | +|-----------------|---------------------------------------------------------------------------|------------------------------------------------| +| **E** Encode | `BeliefEmbedder` + `LinOSSEncoder` projecting beliefs through an oscillatory state-space, blended 50/50 with a SHA-256 binary pattern | `adapters/memevolve/encode.py:36` | +| **U** Store | `AttractorBasin` — Hopfield-style content-addressable store; `InMemoryProvider` for strategy-level persistence | `basins/attractor.py`, `providers/in_memory.py` | +| **R** Retrieve | `retrieve_ranked_memories` — normalized Hopfield overlap, top-k, deterministic tiebreaker; optional curiosity rerank | `adapters/memevolve/retrieve.py:55` | +| **G** Manage | `EvolutionEngine` + `AutoStrategyEvolver` — mutation/selection over a frozen `Strategy` population | `evolution/engine.py`, `evolution/auto_evolver.py` | + +### 3.3 Determinism — the unique property among baselines + +`ElumeMemoryProvider.initialize()` builds a per-instance +`np.random.default_rng(seed)` (never module-global), constructs a session id +deterministically from `SHA-1(seed_bytes ‖ zero_clock_bytes)`, and seeds both +the `LinOSSEncoder` and `BeliefEmbedder`. `retrieve_ranked_memories` uses a +deterministic tiebreaker `(-score, id)`. The doc states the guarantee +explicitly: + +> Given the same `seed` in the config, two full provider lifecycles +> (`initialize → provide_memory × N → take_in_memory × N`) on the same +> platform produce byte-identical `MemoryResponse` sequences at every step. + +Where "same platform" = +`{arch}|{system}|{impl}|{python}|numpy={version}` +(`docs/adapters/memevolve.md:140-145`). + +This is the property that makes Elume the **only** MemEvolve baseline whose +runs can be checksummed end to end. + +### 3.4 Curiosity homing — the algorithmic delta + +Every other MemEvolve baseline does R with uniform-random exploration. Elume +optionally reranks by **information gain** (`provider.py:391`). For each +candidate basin: + +- raw curiosity = `H(belief) · (1 - p_basin)` if known, else `H(belief)` if + unknown (completely unknown basins are most informative). +- normalize raw scores to `[0, 1]` across the candidate set. +- compound = `base_score + λ · normalized_curiosity` if + `normalized_curiosity ≥ threshold`, else `base_score`. + +After each trajectory, `take_in_memory` updates the per-session belief: `+0.1` +to each active basin's mass on success, `-0.05` on failure, then renormalize. +Belief is per-`session_id`. A basin's mass is a posterior over "did this +attractor help last time?" + +This is the bit that makes Elume *not just another baseline*. The other 11–14 +are uniform. Elume homes on epistemic gain. + +--- + +## Part 4 — LinOSS + +### 4.1 The dynamical system + +LinOSS = **Linear Oscillatory State-Space**, from Rusch & Rus (ICLR 2025). It +treats hidden state as a **forced harmonic oscillator**: + +``` +y'' + A y = B u(t) +``` + +with `y` (position) and `z = y'` (velocity). The hidden state is split into +pairs `(y_i, z_i)` that form 2-D rotation blocks; each block has its own +frequency `ω_i = √A_i`. Crucially, **A is constrained non-negative** (`A = +ReLU(Â)`) so each block oscillates rather than decaying or blowing up. The +state stays bounded over arbitrarily long sequences while still capturing +long-range temporal dependence — the failure mode of vanilla RNNs. + +### 4.2 The two discretization schemes + +Both are one-step updates over a positive `dt` (`elume/linoss/solver.py`): + +**Implicit (IM)** — fully implicit, dissipative under no forcing. + +``` +S = (I + dt² A)⁻¹ # Schur complement +z_n = S·z_{n-1} − dt·A·S·y_{n-1} + dt·S·B·u +y_n = S·y_{n-1} + dt·S·z_{n-1} + dt²·S·B·u +``` + +**Implicit-Explicit (IMEX)** — symplectic Euler, energy-preserving under no +forcing. + +``` +z_n = z_{n-1} + dt·(−A·y_{n-1} + B·u_n) +y_n = y_{n-1} + dt·z_n +``` + +Both modes return rich metrics — `energy_before`, `energy_after`, +`energy_delta`, `is_dissipative_step`, `forcing_norm`, +`a_clipped_to_nonnegative` — so a downstream observer can verify the +integration is well-behaved. + +### 4.3 The LinOSSEncoder + +`elume/linoss/encoder.py` wraps the oscillator as a trajectory encoder: + +- `A` is a block-diagonal 2×2 rotation matrix with random frequencies in + `[0.1, 1.0]`, deterministic from `seed`. +- `B` is `0.1 × N(0, I)` projection from input to hidden space. +- Each step: `x ← tanh(A·x·dt_scale + B·u)`. The `tanh` plus + `dt_scale = min(dt, 1.0)` together prevent saturation over long + trajectories. + +### 4.4 Why this fixed a real bug + +The encoder docstring says it directly (`encoder.py:1-31`): + +> This is Elume's kernel-level fix for the dionysus3 trajectory-encoder bug. +> The dionysus3 encoder tokenized query strings and ran them through LinOSS +> one word at a time, using a sequence model as a clunky word embedder and +> throwing away its actual capability. + +The fix has two pieces: + +1. **Type-level**: `encode` accepts `Sequence[TrajectoryStep]` only. There is + a `_guard_trajectory_input` that raises `TypeError` if you pass `str` or + `bytes` — with the bug name in the error message, so the regression cannot + land twice. +2. **Semantic-level**: the encoder integrates *across time* over real + trajectory steps (belief states, basin activations, retrieval outcomes), + with `dt` driving how aggressively the oscillator advances. + +### 4.5 Pre-expansion belief injection + +This is the actual algorithmic delta Elume contributes inside the E stage of a +MemEvolve provider (`adapters/memevolve/encode.py:90-135`): + +1. When a query arrives, build a deterministic float32 `BeliefState` prior + from `SHA-256(query ‖ context)`, normalized to a probability distribution. +2. Wrap that in a single `TrajectoryStep` of kind `BELIEF`. +3. Run the encoder over the one-step trajectory to get a hidden vector. +4. Re-discretize the hidden vector to ±1. +5. 50/50-blend with the SHA-256 base pattern. + +The Hopfield store then receives a key that already carries oscillatory +belief structure, not just query bytes. That is what the upstream +identification dossier called the *"LinOSS belief-state injection — +pre-expansion encoding step."* + +### 4.6 Why LinOSS got its own boundary + +The project CLAUDE.md captures the lesson: + +> `linoss-dynamics` is its own domain-agnostic numerical runtime. Elume may +> delegate raw oscillator stepping to it, but Elume owns `LinOSSEncoder`, +> trajectory records, belief/basin meaning, and replay/memory semantics. Do +> not push Elume-specific encoders into `linoss-dynamics`. + +The math (`solver.py`) is domain-free and belongs in `linoss-dynamics`. The +encoder (`encoder.py`) is Elume-shaped — it knows about `TrajectoryStep`, +`BeliefState`, basin patterns. Two packages, two audiences. + +--- + +## Summary + +1. **MemEvolve** (Zhang et al. 2025, ICML 2026) is upstream OSS. It defines + E / U / R / G as the four memory operations, runs an outer loop with four + phases (Analyze → Generate → Create → Validate), uses Flash Searcher as + the inner-loop runner, and ships under the **EvolveLab** umbrella. The + pluggable surface is `BaseMemoryProvider` with three methods: + `initialize`, `provide_memory` (R), `take_in_memory` (E + U). G runs in + the outer loop between rollouts. + +2. **What is in `dionysus3/api/.../memevolve_*` is not MemEvolve.** It is + **proto-Elume** under a borrowed name from before we knew the upstream + paper existed. Each file under that umbrella is genuinely ours, sometimes + with paper-idea-shaped scaffolding (E/U/R/G genotype, three-method + provider interface) and sometimes purely our delta (12-dim trajectory + feedback, LinOSS belief injection, basin-context enrichment). We did not + import MemEvolve classes for six reasons: substrate-level determinism, + runtime weight, dependency direction, license/attribution clarity, + different unit of evolution, and pre-release upstream. + +3. **Elume** is the OSS extraction. `elume.adapters.memevolve` is the + cartridge that turns Elume into a MemEvolve baseline — the only one with + byte-identical replay across `platform_fingerprint()` and the only one + that homes on epistemic gain. + +4. **LinOSS** is the linear oscillatory state-space math from Rusch & Rus + (ICLR 2025). `linoss/solver.py` holds the IM and IMEX one-step updates of + the forced harmonic oscillator. `linoss/encoder.py` wraps that into a + trajectory encoder over real `TrajectoryStep` sequences — the kernel-level + fix for the dionysus3 bug that fed query strings tokenized + character-by-character into a sequence model. The "LinOSS belief-state + injection" — running a query-derived belief through the encoder *before* + hitting the Hopfield pattern — is the legitimate algorithmic delta Elume + contributes on top of stock MemEvolve baselines. + +The shape of the relationship: **MemEvolve owns the meta-evolution game and +the benchmark harness. Elume owns the substrate that makes one player in that +game deterministic, replayable, and curiosity-driven.** We plug back in via +`BaseMemoryProvider`. We plan to upstream the integration patterns +(12-dim feedback, basin-context enrichment, LinOSS injection hook) where they +generalize.