diff --git a/ATTRIBUTION_CLARITY.md b/ATTRIBUTION_CLARITY.md new file mode 100644 index 0000000..2a472ae --- /dev/null +++ b/ATTRIBUTION_CLARITY.md @@ -0,0 +1,331 @@ +# Attribution Clarity + +**Purpose:** Establish, with code-level evidence, exactly which parts of Elume are +ported from upstream sources and which parts are original Elume engineering. This +document is the authoritative reference for academic citations, licensing +conversations, and any external claim that touches Elume's relationship to +upstream work. + +**Audience:** Reviewers, academic readers, license auditors, future maintainers, +and the author when answering "did you write this or did they?" + +**Last reviewed against:** +- `bingreeky/MemEvolve` reference repo at `/Volumes/Asylum/repos/MemEvolve/Flash-Searcher-main/` +- LinOSS reference repo (Rusch & Rus 2024, JAX/Equinox) at `/Volumes/Asylum/repos/linoss/` + +--- + +## TL;DR + +Three categories of code in Elume relative to upstream: + +1. **Direct port** — code copied (with adaptations) from an upstream Apache-2.0 + source. License attribution preserved. Line citations exist in the file + header. +2. **Interface conformance** — original code that implements an upstream public + interface so Elume can be plugged into upstream's framework. No code shared. +3. **Framing-only** — original code that adopts a *conceptual framing* from an + upstream paper but shares no code with the upstream implementation. Standard + primitives, original engineering. + +A given Elume module sits in exactly one of these three categories. The +boundaries are not fuzzy. + +--- + +## Lineage Table + +| Elume code | Category | Upstream source | License obligation | +|---|---|---|---| +| `elume.adapters.memevolve.shaping` (415 L) | **Direct port** | bingreeky/MemEvolve `EvolveLab/providers/dionysus_memory_provider.py` lines 37–40, 262–319; `EvolveLab/providers/entity_extractor.py` lines 84–92, 135–216, 342–346 | Apache-2.0 attribution preserved at top of file | +| `elume.adapters.memevolve.provider.ElumeMemoryProvider` (534 L) | **Interface conformance** | Implements `EvolveLab.base_memory.BaseMemoryProvider` interface | None (interface implementation; no shared code) | +| `elume.adapters.memevolve.{ingest,encode,retrieve,records}` | **Original** | None — supporting code for the adapter | None | +| `elume.evolution.engine.EvolutionEngine` (192 L) | **Framing-only** | Conceptual framing from Zhang et al. 2025 (arXiv:2512.18746); no code shared with `MemEvolve.core.memory_evolver.MemoryEvolver` | None | +| `elume.evolution.auto_evolver.AutoStrategyEvolver` (110 L) | **Framing-only** | Inspired by the multi-round evolution concept in MemEvolve; no code shared with `MemEvolve.core.auto_evolver.AutoEvolver` | None | +| `elume.evolution.operators.*` (186 L) | **Original** | Standard GA primitives | None | +| `elume.evolution.selection.fitness_tournament` (76 L) | **Original** | Standard GA tournament | None | +| `elume.linoss.*` (593 L total) | **Framing-only** | Algorithm from Rusch & Rus 2024 (arXiv:2410.03943); no code shared with the upstream JAX/Equinox reference | None | +| `linoss-dynamics` package (258 L; separate repo) | **Framing-only** | Same paper as above; pure NumPy implementation | None | +| `elume.basins.*` | **Framing-only** | Hopfield 1982; standard associative memory primitives | None | + +--- + +## Per-Module Evidence + +### 1. `elume.adapters.memevolve.shaping` — Direct port + +**Status:** Code adapted from `bingreeky/MemEvolve` (Apache-2.0). + +**Header in source file:** +```python +# Portions adapted from bingreeky/MemEvolve (Apache-2.0); see ATTRIBUTION.md. +""" +Sources adapted (Apache-2.0): + - EvolveLab/providers/dionysus_memory_provider.py lines 37–40, 262–319 + - EvolveLab/providers/entity_extractor.py lines 84–92, 135–216, 342–346 +""" +``` + +**Verified against upstream:** + +Upstream `EvolveLab/providers/dionysus_memory_provider.py:37-40`: +```python +MEMEVOLVE_PHASE_MEMORY_TYPES = { + MemoryStatus.BEGIN: ["strategic", "procedural", "semantic"], + MemoryStatus.IN: ["episodic", "context", "semantic"], +} +``` + +Elume `src/elume/adapters/memevolve/shaping.py`: +```python +PHASE_MEMORY_TYPES: dict[str, list[str]] = { + "begin": ["strategic", "procedural", "semantic"], + "in": ["episodic", "context", "semantic"], +} +``` + +The data is identical. The variable name was de-prefixed (the surrounding module +path already says `memevolve`) and the enum keys were stringified to remove the +upstream `MemoryStatus` dependency. **This is a port. Attribution is preserved.** + +Same verification holds for `PII_PATTERNS` (upstream `entity_extractor.py:84-92`) +and the tool/source patterns. + +**Approved phrasing:** +- "We adapt the phase-memory-type mapping and PII patterns from MemEvolve under + Apache-2.0 (see ATTRIBUTION.md)." +- "The shaping helpers in `elume.adapters.memevolve.shaping` are ported from + bingreeky/MemEvolve." + +**Prohibited phrasing:** +- "We wrote our own PII patterns." (False — they're ported.) + +--- + +### 2. `elume.adapters.memevolve.provider.ElumeMemoryProvider` — Interface conformance + +**Status:** Original code that implements upstream's `BaseMemoryProvider` +interface so Elume can be plugged into MemEvolve's benchmark suite as a +`--memory_provider` choice. + +**Upstream interface** (`EvolveLab/base_memory.py:10`): +```python +class BaseMemoryProvider(ABC): + ... +``` + +**Elume implementation:** 534 lines, original, no shared code with any upstream +provider. Distinguished from upstream's own providers (`DionysusMemoryProvider`, +`AgentKBProvider`, `VoyagerMemoryProvider`, etc.) by the `Elume` prefix. + +The relationship is identical to writing a `dict`-compatible class in Python: +you implement the interface methods, you do not port `dict`. + +**Approved phrasing:** +- "ElumeMemoryProvider conforms to MemEvolve's `BaseMemoryProvider` interface." +- "The first deterministic baseline in MemEvolve's `--memory_provider` list." + +**Prohibited phrasing:** +- "ElumeMemoryProvider is a port of MemEvolve's provider." (False — it's + original code that implements the upstream public interface.) + +--- + +### 3. `elume.evolution.engine.EvolutionEngine` — Framing-only + +**Status:** Original Elume engineering. Adopts the conceptual *framing* from +Zhang et al. 2025 — "agent memory as an evolvable population rather than policy +weights" — but shares no code with the upstream implementation. + +**Upstream class with the closest functional role** +(`MemEvolve/core/memory_evolver.py:27`): +```python +class MemoryEvolver: + """Core memory evolution orchestrator""" + # Imports: openai, dotenv, MemorySystemCreator, PhaseAnalyzer, + # PhaseGenerator, PhaseValidator +``` + +Upstream `MemoryEvolver` is an **LLM-driven multi-phase orchestrator**: it calls +GPT-class models to analyze, generate, and validate memory systems. It depends +on `openai` and a multi-phase pipeline of `PhaseAnalyzer` / +`PhaseGenerator` / `PhaseValidator`. + +**Elume `EvolutionEngine`** is a **pure deterministic GA stepper**: it reads +strategies from a `MemoryProvider`, applies tournament selection plus mutation +operators, and writes children back. It depends on `numpy` only. There is no +LLM call, no multi-phase pipeline, no analyzer/validator concept. + +**Code shared between the two:** zero lines. + +**Engineering contributions specific to Elume `EvolutionEngine`:** +- Byte-identical replay (injectable `numpy.random.Generator`, deterministic + seed) +- Immutable `Strategy` records — `frozen=True`, every mutation produces a new + child via `.evolved()` with `parent_name` set +- Provider boundary — engine holds no in-memory population; all reads/writes go + through `MemoryProvider` +- Composable `MutationOperator` / `CrossoverOperator` protocols +- Fitness scores live outside the `Strategy` model (in caller-supplied dicts) + to preserve immutability invariants + +**The framing reference**, which is what Elume adopts from MemEvolve, is one +sentence at the conceptual level: *agent memory should be evolvable rather than +just trained*. Elume's implementation of that idea uses standard 1990s-era +genetic algorithm primitives (Holland-style tournament, elitism, mutation +operators) that predate MemEvolve by decades. + +**Approved phrasing:** +- "We implement a MemEvolve-style evolution loop." +- "Elume adopts the evolvable-memory-population framing from Zhang et al. 2025; + the implementation is original Elume work using standard GA primitives." +- "Elume contributes the engineering substrate: byte-identical replay, immutable + lineage, provider abstraction, and composable operator protocols." + +**Prohibited phrasing:** +- "We ported MemEvolve's evolution engine." (**False.** No code is shared.) +- "Elume reimplements MemoryEvolver." (**False.** Different abstraction — + upstream is LLM/multi-phase; Elume is GA/deterministic.) +- "Elume's `EvolutionEngine` is based on MemEvolve's." (**Misleading.** The + framing is, the code is not.) + +--- + +### 4. `elume.evolution.auto_evolver.AutoStrategyEvolver` — Framing-only (renamed) + +**Status:** Original Elume engineering. **Renamed from `AutoEvolver` to +`AutoStrategyEvolver` in v0.3.0** to eliminate name collision with upstream's +`MemEvolve.core.auto_evolver.AutoEvolver`, which has identical name but +opposite-weight-class semantics. + +**Why the rename was necessary:** + +| | Upstream `AutoEvolver` | Elume `AutoStrategyEvolver` | +|---|---|---| +| Role | Dataset benchmark orchestrator | Pure GA loop wrapper | +| Dependencies | LLMs (GPT-5), ThreadPoolExecutor, dotenv, datasets, dataset configs | numpy only | +| Constructor args | 15+ (`analysis_model_id`, `gen_model_id`, `work_root`, `dataset_name`, `run_provider`, `creativity_index`, `task_batch_x`, `top_t`, `max_workers`, `use_pareto_selection`, …) | 4 (`engine`, `fitness_fn`, `max_generations`, `stop_on_plateau`) | +| Behavior | Runs N rounds against eval datasets with concurrent workers | Loops `evolve_one_generation` until plateau | +| Lines | ~80 just for constructor signatures | 110 total | + +A reader who saw `AutoEvolver(engine, fitness_fn)` and assumed it behaved like +the paper's `AutoEvolver(analysis_model_id=..., dataset_name="gaia")` would get +wildly different results. Namespace alone (`elume.evolution.auto_evolver` vs +`MemEvolve.core.auto_evolver`) handles import disambiguation but does not +prevent reader confusion when the class name appears in prose, talks, papers, +or stack traces. + +**Renaming policy applied:** Where Elume's class name collides with an upstream +class of the same name with materially different semantics, the Elume name is +disambiguated. Where the name does not collide, namespace alone is sufficient +(the `pandas.DataFrame` vs `polars.DataFrame` precedent). + +**Code shared between upstream `AutoEvolver` and Elume `AutoStrategyEvolver`:** +zero lines. + +**Approved phrasing:** +- "Elume's `AutoStrategyEvolver` runs a deterministic generation loop over an + `EvolutionEngine`." +- "The class is a thin wrapper that evaluates fitness and detects plateaus — + it is not equivalent to MemEvolve's heavyweight `AutoEvolver`." + +--- + +### 5. `elume.linoss` and `linoss-dynamics` — Framing-only + +**Status:** Original implementations of the LinOSS algorithm from Rusch & Rus +2024 (arXiv:2410.03943). No code shared with the JAX/Equinox upstream reference +implementation. + +**Upstream reference** (`/Volumes/Asylum/repos/linoss/models/LinOSS.py`): +- `class GLU(eqx.Module)`, `class LinOSSLayer(eqx.Module)`, + `class LinOSSBlock(eqx.Module)`, `class LinOSS(eqx.Module)` — Equinox neural + network layers +- `def apply_linoss_im(...)`, `def apply_linoss_imex(...)` — JAX scan operations +- Dependencies: `equinox`, `jax`, `jax.numpy` + +**Elume / linoss-dynamics:** +- `def linoss_step(...)`, `def damped_linoss_step(...)`, `def linoss_step_impl` + — pure NumPy step functions +- `def energy(...)`, `def delta_energy(...)`, `def convergence_window(...)` — + energy diagnostics not present in upstream +- `class LinOSSError`, `class InvalidShapeError`, `class InvalidDampingError`, + `class UnsupportedModeError` — error hierarchy not in upstream +- Dependencies: `numpy` only + +**Class name collision:** None. Upstream uses `LinOSSLayer`, `LinOSSBlock`, +`LinOSS` (Equinox modules); Elume / linoss-dynamics uses functions and error +classes. The two APIs do not overlap. + +**Approved phrasing:** +- "Elume implements the LinOSS algorithm from Rusch & Rus 2024." +- "`linoss-dynamics` provides a pure-NumPy reference implementation with energy + diagnostics." + +**Prohibited phrasing:** +- "Elume ports the LinOSS reference implementation." (**False.** Upstream is + JAX/Equinox; Elume is NumPy. Same algorithm, different code.) + +--- + +## Naming Policy (canonical) + +When Elume code shares a name with upstream, the policy is: + +1. **If Elume code is a port** → use the upstream name (after de-prefixing for + namespace cleanliness). Lineage should be obvious. +2. **If Elume code conforms to an upstream interface** → distinguish with a + clear prefix (`Elume…`). +3. **If Elume code is original but the name happens to match upstream**: + - Same general semantics → namespace handles it (e.g. `pandas.DataFrame` + precedent). + - Materially different semantics → **rename**. The collision is the bug. + +The `AutoEvolver` → `AutoStrategyEvolver` rename in v0.3.0 is the only case +where this policy required action. Namespace handles all other cases. + +--- + +## Defensive Phrasings (paste into papers / READMEs verbatim) + +### Acceptable + +> Elume's evolution module is a deterministic, replay-safe genetic algorithm +> operating on immutable `Strategy` records through a provider boundary. The +> framing — agent memory as an evolvable population rather than policy weights +> — is adopted from MemEvolve (Zhang et al. 2025, arXiv:2512.18746). The +> implementation is original Elume work using standard GA primitives. What +> Elume contributes is the engineering substrate: byte-identical replay, +> immutable lineage, provider abstraction, and composable operator protocols. + +> The `elume.adapters.memevolve.shaping` module includes helpers ported from +> bingreeky/MemEvolve under Apache-2.0 (see `ATTRIBUTION.md`). All other +> evolution code in Elume is original. + +> Elume implements the LinOSS algorithm (Rusch & Rus 2024) in pure NumPy with +> energy diagnostics. The reference upstream implementation is in JAX/Equinox; +> no code is shared between the two. + +### Unacceptable + +- "Elume is a port of MemEvolve." (False.) +- "Elume reimplements the MemEvolve evolution engine." (False — only the + framing is adopted.) +- "Elume contains MemEvolve code." (Partially false — only `shaping.py` + contains ported MemEvolve code; everything else is original.) + +--- + +## Maintenance + +- When adding new code to `elume.evolution`, `elume.linoss`, `elume.basins`, or + `elume.adapters.memevolve`, update the lineage table above. +- When adopting new code from any upstream source, add the file header citation + pattern used in `shaping.py` and update this document and `ATTRIBUTION.md`. +- When a new collision-risk class name is introduced, run the audit: + `grep "^class " /Volumes/Asylum/repos/MemEvolve/`. If a match appears, + apply the naming policy above. + +--- + +*Authored: 2026-05-05. Author: Mani Saint-Victor (drmani215@gmail.com).* diff --git a/CHANGELOG.md b/CHANGELOG.md index 6207d11..8abf777 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,96 @@ All notable changes to Elume are documented here. Format loosely follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versions follow semantic versioning once `0.1.0` ships; pre-alpha releases may break anything. +## [0.3.0] — 2026-05-05 — Cognitive substrate recategorization + linoss-dynamics dependency edge + +v0.3.0 reframes Elume from "agentic memory engine" to its honest description: +**a deterministic, replay-safe substrate for cognitive-architecture agents** — +mental models, attractor memory, temporal encoding, metacognitive control, and +strategy evolution behind clean provider boundaries. Memory is the entry point; +mental modeling is the architecture; replay-safety is the engineering contribution. + +This release also establishes the dependency edge to the standalone +[`linoss-dynamics`](https://github.com/bionicbutterfly13/linoss-dynamics) package +(now on PyPI as a peer library) and renames the upstream-collision-prone +`AutoEvolver` class. + +### Changed — Recategorization (no code change) + +- README headline, "What Elume is" section, and public descriptors updated to + reflect the cognitive-substrate framing. +- ELUME acronym updated: **E**volvable, **L**ong-horizon, **U**nified, + **M**ental-model, **E**ngine. +- `pyproject.toml` description and keywords expanded to surface mental-model, + metacognition, and cognitive-architecture terms. +- `authors` field now includes email (`drmani215@gmail.com`). + +### Added — `ATTRIBUTION_CLARITY.md` + +Authoritative, code-level reference document distinguishing three categories of +Elume code: + +1. **Direct port** (`elume.adapters.memevolve.shaping`) — Apache-2.0 code + adapted from bingreeky/MemEvolve with line citations. +2. **Interface conformance** (`ElumeMemoryProvider`) — original code that + implements an upstream public interface. +3. **Framing-only** (`elume.evolution.*`, `elume.linoss.*`, `elume.basins.*`) + — original Elume engineering that adopts conceptual framings from upstream + papers but shares no code with reference implementations. + +Includes approved/prohibited phrasings for academic and licensing contexts. + +### Added — `linoss-dynamics` dependency edge + +- New runtime dependency: `linoss-dynamics>=0.1.0`. +- Establishes the cleaner architectural layering: `linoss-dynamics` (pure NumPy + physics) → `elume` (cognitive substrate) → application layers. +- v0.3.0 leaves `elume.linoss` in place (numerical behavior pinned by 1177 + passing tests). Code-level consolidation between `elume.linoss` and + `linoss-dynamics` is planned for v0.4.0. +- Consumers can now `from linoss_dynamics import energy, delta_energy, + convergence_window` directly to access diagnostic helpers not exposed by + `elume.linoss`. + +### Changed — `AutoEvolver` → `AutoStrategyEvolver` (BREAKING) + +Renamed `elume.evolution.auto_evolver.AutoEvolver` to `AutoStrategyEvolver`. + +**Why:** The upstream MemEvolve repo (`bingreeky/MemEvolve`, +`MemEvolve/core/auto_evolver.py:37`) defines a class named `AutoEvolver` with +identical name but materially different semantics — it is a heavyweight, +LLM-driven, dataset-evaluation orchestrator (15+ constructor args, GPT-5 +calls, ThreadPoolExecutor concurrency). Elume's `AutoEvolver` was the +opposite: a deterministic, 4-arg GA loop wrapper. + +Same name, opposite weight class. A reader who saw `AutoEvolver(engine, +fitness_fn)` and assumed it behaved like the paper's +`AutoEvolver(analysis_model_id=...)` would have gotten wildly different +behavior. Namespace alone (`elume.evolution.auto_evolver` vs +`MemEvolve.core.auto_evolver`) handled import disambiguation but did not +prevent reader confusion in prose, talks, papers, or stack traces. + +The rename is the only naming-policy action this release. All other +potentially-shared names (`EvolutionEngine`, `MutationOperator`, +`fitness_tournament`, etc.) either have no upstream collision or are +disambiguated by namespace alone — see `ATTRIBUTION_CLARITY.md` § Naming +Policy. + +**Migration:** + +```python +# Before +from elume.evolution import AutoEvolver +auto = AutoEvolver(engine, fitness_fn, max_generations=10) + +# After +from elume.evolution import AutoStrategyEvolver +auto = AutoStrategyEvolver(engine, fitness_fn, max_generations=10) +``` + +The class signature, behavior, and tests are unchanged. Only the name moves. + +--- + ## [0.2.0] — 2026-05-04 — MemEvolve cartridge + curiosity homing device v0.2.0 ships the MemEvolve cartridge (`elume.adapters.memevolve.ElumeMemoryProvider`) @@ -14,7 +104,7 @@ re-acquire the search heading inside MemEvolve's loop. Empirical A/B signal: 4 of 5 retrieval steps re-rank between `curiosity=False` and `curiosity=True` on the synthetic fixture (seed=42). -### Added — Track 023: MemEvolve Cartridge (Fleet B, planned-in-progress) +### Added — Track 023: MemEvolve Cartridge (Fleet B) - `elume.adapters.memevolve.ElumeMemoryProvider` — a fully conformant `BaseMemoryProvider` implementation backed by Elume's `AttractorBasin`, @@ -34,7 +124,7 @@ on the synthetic fixture (seed=42). module-level global. - Consumer guide: `docs/adapters/memevolve.md`. -### Added — Track 024: Curiosity Homing Device (Fleet B, planned-in-progress) +### Added — Track 024: Curiosity Homing Device (Fleet B) - `elume.cognition.curiosity`: - `CuriosityScore` — frozen dataclass: `information_gain`, `epistemic_value`, @@ -54,7 +144,7 @@ on the synthetic fixture (seed=42). - Ported from dionysus3 `CuriosityDriveService` (`mosaeic_self_discovery.py:300-443`) and `arousal_system_service.py:44-143` — FastAPI/Pydantic/singletons stripped. -### Added — Track 025: Hyperevolution Wiring (Fleet B, planned-in-progress) +### Added — Track 025: Hyperevolution Wiring (Fleet B) - `ElumeMemoryProvider` gains `curiosity: bool = False` config flag. - Retrieval bias: with `curiosity=True`, basin scores are re-ranked by @@ -317,7 +407,7 @@ clean, GitHub CI green on Python 3.11/3.12, MIT-licensed. mutation + provider persistence), `evaluate` (fitness_fn convenience). Uses `MemoryProvider` as the sole persistence boundary; never deletes historical strategies. -- `elume.evolution.auto_evolver` — `AutoEvolver` class: multi-generation +- `elume.evolution.auto_evolver` — `AutoStrategyEvolver` class: multi-generation runs with plateau detection, returning the final population sorted by fitness. - 96 new tests (22 operator + 15 selection + 14 engine + 6 auto-evolver diff --git a/README.md b/README.md index be31acc..f8a8e94 100644 --- a/README.md +++ b/README.md @@ -5,21 +5,38 @@ [![CI](https://github.com/bionicbutterfly13/elume/actions/workflows/ci.yml/badge.svg)](https://github.com/bionicbutterfly13/elume/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -**An open-source agentic memory engine for long-horizon adaptive learning.** +**Agentic memory with mental-model architecture — a deterministic, replay-safe substrate for cognitive-architecture agents.** -Elume brings together existing memory and sequence-modeling components into a single working system for long-horizon agents. +Elume is the cognitive substrate underneath an agent: long-horizon temporal encoding, attractor-based associative memory, mental-model primitives, metacognitive control, and deterministic strategy evolution — all behind clean provider boundaries, all replay-safe by construction. -It integrates LinOSS-style long-horizon temporal encoding (Rusch & Rus, ICLR 2025), attractor-based associative memory, and a deterministic adaptive memory substrate into one open-source stack. The contribution of Elume is not the invention of these underlying methods in isolation, but the engineering work required to combine them, adapt their codepaths, and make them operate coherently in a unified memory system. +Memory is the entry point. Mental modeling is the architecture. Replay-safety is the engineering contribution. + +It integrates LinOSS-style temporal encoding (Rusch & Rus, ICLR 2025), Hopfield-style associative memory, mental-model and metacognitive record types, and a deterministic evolution substrate into one open-source stack. The contribution of Elume is not the invention of the underlying methods in isolation, but the engineering work required to combine them, adapt their codepaths, and make them operate coherently inside a single deterministic kernel. ## What Elume is -Elume is an integration layer and runtime memory stack for agents that need to: +Elume is a runtime cognitive substrate for agents that need to: + +- encode long trajectories with oscillatory state-space dynamics, +- recover useful prior state through attractor-based associative recall, +- maintain explicit mental models with predictions and revisions, +- exercise metacognitive control over inference and action selection, +- and evolve memory strategies over time, deterministically. + +The full primitive set: -- encode long trajectories, -- recover useful prior state through associative recall, -- and adapt memory behavior over time. +| Layer | Modules | Role | +|---|---|---| +| **Temporal encoding** | `elume.linoss` | LinOSS solver, encoder, timing | +| **Memory substrate** | `elume.basins`, `elume.network` | Attractor field, Hopfield, self-modeling network | +| **Mental modeling** | `elume.models.mental_model`, `elume.cognition.mental_model` | `MentalModel`, `BasinRelationship`, `PredictionTemplate`, `ModelPrediction`, `ModelRevision`, mental-model subnetworks | +| **Metacognition** | `elume.models.metacognitive` | `CognitiveCore`, `MetacognitiveParticle`, `MentalAction`, `ParticleType` | +| **Belief & cognition** | `elume.models.belief`, `elume.models.cognitive`, `elume.cognition` | Belief states, cognitive events, deterministic thought competition, prior-gated cognition, curiosity homing | +| **Evolution** | `elume.evolution` | Strategy lifecycle, GA over immutable `Strategy` records | +| **Determinism** | `elume.envelope` | Canonical pre-image hashing, byte-identical replay | +| **Integration** | `elume.providers`, `elume.adapters`, `elume.embedders` | Provider contracts, MemEvolve cartridge, embedding protocols | -In practice, Elume packages and stabilizes multiple upstream ideas and implementations so they can be used together as a single agent memory engine. +In practice, Elume packages and stabilizes multiple upstream ideas plus original cognitive-architecture engineering so they can be used together as one substrate. ## What Elume is not @@ -210,12 +227,13 @@ pip install -e /path/to/elume ## On the name -**Elume** is the brand form. **ELUME** works as an acronym mnemonic — Evolving, Long-horizon, Unified, Memory, Engine. +**Elume** is the brand form. **ELUME** works as an acronym mnemonic — Evolvable Long-horizon Unified Mental-model Engine. For public descriptors: -- **Short:** *Agentic Memory Engine* -- **Technical long form:** *Long-Horizon Adaptive Memory Engine* -- **Tagline:** *An open-source agentic memory engine for long-horizon adaptive learning.* +- **Short:** *Cognitive Substrate for Agents* +- **Memory-first framing (for SaaS/category fit):** *Agentic Memory with Mental-Model Architecture* +- **Technical long form:** *Deterministic, Replay-Safe Substrate for Cognitive-Architecture Agents* +- **Tagline:** *Agentic memory with mental-model architecture — a deterministic, replay-safe substrate for cognitive-architecture agents.* ## License diff --git a/conductor/tracks/009-evolution-engine/plan.md b/conductor/tracks/009-evolution-engine/plan.md index 43e8dfa..e2b139a 100644 --- a/conductor/tracks/009-evolution-engine/plan.md +++ b/conductor/tracks/009-evolution-engine/plan.md @@ -37,7 +37,7 @@ TDD discipline: contract + unit tests first, confirm RED, then build operators - [ ] **3.1** `tests/integration/test_evolution_end_to_end.py` - Seed 4 strategies with varying `genotype["temperature"]`. - - Use `AutoEvolver` with `fitness_fn = lambda s: -abs(s.genotype["temperature"] - 0.7)`. + - Use `AutoStrategyEvolver` with `fitness_fn = lambda s: -abs(s.genotype["temperature"] - 0.7)`. - Run for 5 generations. - Assert best strategy improves vs seed. - Assert seed strategies are still present. @@ -71,7 +71,7 @@ TDD discipline: contract + unit tests first, confirm RED, then build operators ## Phase 8 — Auto-evolver (GREEN step 4) - [ ] **8.1** `src/elume/evolution/auto_evolver.py` - - `AutoEvolver` class with `run`. + - `AutoStrategyEvolver` class with `run`. - Plateau detection. - **Verified:** auto-evolver tests + integration test pass. diff --git a/conductor/tracks/009-evolution-engine/spec.md b/conductor/tracks/009-evolution-engine/spec.md index b331f64..e417b20 100644 --- a/conductor/tracks/009-evolution-engine/spec.md +++ b/conductor/tracks/009-evolution-engine/spec.md @@ -12,7 +12,7 @@ This is a Fleet B genuine rewrite. The dionysus3 engine is glanced at for API-sh - Mutation operators that produce successors via `Strategy.evolved(...)` — never mutate parents. - Selection primitives (`fitness_tournament`, `top_k`) that operate on frozen `Strategy` sequences and return selected references, never mutated copies. - An `EvolutionEngine` class that uses a `MemoryProvider` as its persistence boundary, runs single-generation steps with fitness scores supplied by the caller, and preserves historical strategies (no deletion of superseded parents). -- An `AutoEvolver` that wraps the engine and runs N generations automatically, stopping on plateau. +- An `AutoStrategyEvolver` that wraps the engine and runs N generations automatically, stopping on plateau. - Full contract test coverage proving the successor invariants hold across many random seeds. - Integration test demonstrating end-to-end evolution with a toy fitness function on an `InMemoryProvider`. - Explicit attribution to Zhang et al. 2025 (MemEvolve) as the methodological source, with a clear note that this is a MemEvolve-*style* implementation, not a direct paper port. @@ -61,9 +61,9 @@ This is a Fleet B genuine rewrite. The dionysus3 engine is glanced at for API-sh - Module docstring: cites Zhang et al. 2025 as methodology, notes this is MemEvolve-*style* not a direct paper port, cites integration-not-invention principle. ### Auto-evolver (`elume/evolution/auto_evolver.py`) -- `AutoEvolver` class: +- `AutoStrategyEvolver` class: ```python - AutoEvolver( + AutoStrategyEvolver( engine: EvolutionEngine, fitness_fn: Callable[[Strategy], float], *, @@ -81,7 +81,7 @@ This is a Fleet B genuine rewrite. The dionysus3 engine is glanced at for API-sh - `MutationOperator`, `CrossoverOperator` (both protocols) - `GenotypeJitterOperator`, `ParameterToggleOperator`, `CrossoverOperator` (concrete — note naming collision; the concrete class is named `UniformCrossoverOperator` to avoid shadowing the protocol name) - `fitness_tournament`, `top_k` -- `EvolutionEngine`, `AutoEvolver` +- `EvolutionEngine`, `AutoStrategyEvolver` ## Out of Scope - **Population garbage collection / generation windows** — the engine never deletes historical strategies. Follow-up track. @@ -94,7 +94,7 @@ This is a Fleet B genuine rewrite. The dionysus3 engine is glanced at for API-sh - **A direct port of the MemEvolve paper** — this is MemEvolve-*style* (population + mutation + selection + successor lineage), not a reproduction of Zhang et al.'s exact algorithm. ## Integration (IO Map) -- **Inlet:** consumers with an initial seed population of `Strategy` instances and a `fitness_fn`. Consumers typically instantiate an `InMemoryProvider`, seed it via the engine, and call `AutoEvolver.run()`. +- **Inlet:** consumers with an initial seed population of `Strategy` instances and a `fitness_fn`. Consumers typically instantiate an `InMemoryProvider`, seed it via the engine, and call `AutoStrategyEvolver.run()`. - **Processing:** population → fitness evaluation → top-k elite retention → tournament selection → operator application → successor children → provider save. - **Outlet:** provider gets new `Strategy` instances appended via `save_strategy`. Returned list is the new generation or final sorted population. - **Host:** `elume.evolution` is the single import path. Depends on `elume.models.Strategy` and `elume.providers.MemoryProvider`. Uses `numpy.random.Generator` for RNG. @@ -129,7 +129,7 @@ These are intentionally separate protocols because the `apply` signatures are in ### Operator produces new name via parent + suffix Each operator generates the child's name as `f"{parent.name}.{op_tag}.{suffix}"` where `suffix` is 8 hex chars from `rng.bytes(4)`. Deterministic under seed, human-readable, collision-safe in practice for populations up to ~10^6. The operator tag makes provenance obvious when inspecting lineage chains. -### AutoEvolver plateau detection +### AutoStrategyEvolver plateau detection Plateau is detected by tracking the best-so-far fitness across generations. If the best has not strictly improved for `stop_on_plateau` consecutive generations, the loop exits. A single generation with exactly the same best-so-far fitness counts as one plateau generation. `stop_on_plateau=0` disables plateau detection. ### Attribution and integration-not-invention @@ -152,14 +152,14 @@ Across many random seeds (parameterized by `pytest.mark.parametrize`), prove: - `tests/unit/test_auto_evolver.py` — runs N generations, plateau detection, sorted final population, toy fitness function correctness. ### Integration test (`tests/integration/test_evolution_end_to_end.py`) -Seed a tiny population of 4 strategies with `genotype={"temperature": x}` for various `x` values. Run `AutoEvolver` with `fitness_fn = lambda s: -abs(s.genotype["temperature"] - 0.7)` for 5 generations. Assert: +Seed a tiny population of 4 strategies with `genotype={"temperature": x}` for various `x` values. Run `AutoStrategyEvolver` with `fitness_fn = lambda s: -abs(s.genotype["temperature"] - 0.7)` for 5 generations. Assert: - The best strategy in the final population has fitness strictly greater than the best in the seed population. - The lineage chain is intact (every non-root strategy's `parent_name` resolves in the provider). - The seed strategies are still present in the provider (no deletion). ## Success Criteria - `elume/evolution/operators.py`, `selection.py`, `engine.py`, `auto_evolver.py` exist with full type annotations and no `Any` in public signatures. -- `from elume.evolution import EvolutionEngine, AutoEvolver, GenotypeJitterOperator, ParameterToggleOperator, UniformCrossoverOperator, fitness_tournament, top_k, MutationOperator, CrossoverOperator` works. +- `from elume.evolution import EvolutionEngine, AutoStrategyEvolver, GenotypeJitterOperator, ParameterToggleOperator, UniformCrossoverOperator, fitness_tournament, top_k, MutationOperator, CrossoverOperator` works. - All contract, unit, and integration tests pass. - Full `pytest` suite is green — 430 prior tests still pass, plus new tests on top. - `ruff check src tests` is clean. diff --git a/docs/archon-readiness/07-determinism-evolution.md b/docs/archon-readiness/07-determinism-evolution.md index eab0ffe..5339254 100644 --- a/docs/archon-readiness/07-determinism-evolution.md +++ b/docs/archon-readiness/07-determinism-evolution.md @@ -18,7 +18,7 @@ The evolution subsystem (MemEvolve-style mutation, selection, engine, and auto-e | `selection.py` | `top_k()` | Pure sorting (no RNG) | ✓ Deterministic | ✓ PASS | | `engine.py` | `EvolutionEngine.__init__()` | `np.random.default_rng(seed)` fallback | ✓ Deterministic | ✓ PASS | | `engine.py` | `evolve_one_generation()` | `rng.integers(0, len)` for operator pick, passes `self._rng` to all calls | ✓ Deterministic | ✓ PASS | -| `auto_evolver.py` | `AutoEvolver.run()` | Delegates to `engine.evolve_one_generation()` | ✓ Deterministic | ✓ PASS | +| `auto_evolver.py` | `AutoStrategyEvolver.run()` | Delegates to `engine.evolve_one_generation()` | ✓ Deterministic | ✓ PASS | ## Key Findings @@ -119,8 +119,8 @@ Given a seeded engine, initial population, and fitness function, **rerunning `ev - Verify that custom `MemoryProvider` implementations don't emit timestamps or randomness on `save_strategy()`. - Status: Already enforced by provider protocol; add a note to `MemoryProvider` docstring. -3. **AutoEvolver Logging** (Priority: LOW) - - If `AutoEvolver` gains logging or introspection, ensure it uses deterministic key material (seed, strategy names) not timestamps. +3. **AutoStrategyEvolver Logging** (Priority: LOW) + - If `AutoStrategyEvolver` gains logging or introspection, ensure it uses deterministic key material (seed, strategy names) not timestamps. - Status: Not an issue in current code, but design note for future extensions. ## Verdict diff --git a/docs/archon-readiness/16-public-api-surface.md b/docs/archon-readiness/16-public-api-surface.md index d9971a5..b177a69 100644 --- a/docs/archon-readiness/16-public-api-surface.md +++ b/docs/archon-readiness/16-public-api-surface.md @@ -60,7 +60,7 @@ Executable operations: classes, functions, and protocols. These define the kerne **Embedders**: `Embedder` (protocol), `BeliefEmbedder`, `CognitiveEventEmbedder` -**Evolution**: `EvolutionEngine`, `AutoEvolver`, `UniformCrossoverOperator`, `GenotypeJitterOperator`, `ParameterToggleOperator`, `fitness_tournament`, `top_k` +**Evolution**: `EvolutionEngine`, `AutoStrategyEvolver`, `UniformCrossoverOperator`, `GenotypeJitterOperator`, `ParameterToggleOperator`, `fitness_tournament`, `top_k` **LinOSS**: `LinOSSEncoder`, `LinOSSTimingIntegration`, `AnomalyThresholdAdapter`, `DecayCadenceModulator`, `RetrievalLatencyBias`, `linoss_step_impl` @@ -86,4 +86,3 @@ Executable operations: classes, functions, and protocols. These define the kerne 2. **Helper functions** (`compute_basin_stability`, `hebbian_reinforcement_delta`, `step_states`) — internal utilities for mechanism implementations 3. **Tuning constants** (`DEFAULT_MAX_NESTING_DEPTH`, `HOPFIELD_MAX_RECALL_ITER`) — kernel-level configuration, not client-facing 4. **Phase 2 placeholders** (`MentalModelSubnetwork`, `FlowState`, `LinOSSMode`) — reserved but unused - diff --git a/docs/plans/phase-2-proposal-review.md b/docs/plans/phase-2-proposal-review.md index 0aadc99..e228ba3 100644 --- a/docs/plans/phase-2-proposal-review.md +++ b/docs/plans/phase-2-proposal-review.md @@ -30,7 +30,7 @@ Elume today is a pre-alpha kernel package with six shipped subsystems, not five. | `elume.linoss` | `LinOSSEncoder`, solver, ACT-R timing modulators | | `elume.basins` | `HopfieldNetwork`, `AttractorBasin`, basin field dynamics | | `elume.embedders` | `Embedder` protocol, reference `BeliefEmbedder` | -| `elume.evolution` | `EvolutionEngine`, `AutoEvolver`, mutation operators, selection | +| `elume.evolution` | `EvolutionEngine`, `AutoStrategyEvolver`, mutation operators, selection | | `elume.providers` | `MemoryProvider` protocol, `InMemoryProvider` reference implementation | **Repo facts verified on April 12, 2026:** diff --git a/docs/plans/phase-2-proposal.md b/docs/plans/phase-2-proposal.md index 6e5b876..3a4d160 100644 --- a/docs/plans/phase-2-proposal.md +++ b/docs/plans/phase-2-proposal.md @@ -20,7 +20,7 @@ Elume is a standalone Python kernel extracted from dionysus3 that unifies three | `elume.linoss` | LinOSS encoder + solver (IM/IMEX discretization) + ACT-R timing modulators | | `elume.basins` | Hopfield network + coupled oscillator basin field + attractor basin core math | | `elume.embedders` | Embedder Protocol + reference BeliefEmbedder | -| `elume.evolution` | EvolutionEngine + AutoEvolver + mutation operators + MemoryProvider + InMemoryProvider | +| `elume.evolution` | EvolutionEngine + AutoStrategyEvolver + mutation operators + MemoryProvider + InMemoryProvider | **What Elume can't do yet:** It records memory and evolves strategies, but it does not *think*. It has no concept of a mental model, a thought, a cognitive event, or an archetypal prior. Phase 2 adds these. diff --git a/pyproject.toml b/pyproject.toml index 1304445..5784506 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,15 +4,15 @@ build-backend = "hatchling.build" [project] name = "elume" -version = "0.2.0" -description = "Elume — an open-source agentic memory engine for long-horizon adaptive learning. An integration layer bringing together LinOSS oscillatory state-space models, attractor-based associative memory, the MemEvolve cartridge (ElumeMemoryProvider), and curiosity homing (shannon-entropy information-gain steering) into a single unified memory kernel. The contribution is integration, not invention." +version = "0.3.0" +description = "Agentic memory with mental-model architecture. Deterministic, replay-safe substrate for cognitive-architecture agents: LinOSS temporal encoding, Hopfield attractor memory, mental-model and metacognitive primitives, strategy evolution, pluggable MemEvolve benchmark adapter. Contribution: byte-identical replay, immutable lineage, provider abstraction, composable operators." readme = "README.md" requires-python = ">=3.11" license = { text = "MIT" } authors = [ - { name = "Mani Saint-Victor" }, + { name = "Mani Saint-Victor", email = "drmani215@gmail.com" }, ] -keywords = ["memory", "active-inference", "attractor-basins", "linoss", "self-evolving"] +keywords = ["memory", "mental-model", "cognitive-architecture", "active-inference", "metacognition", "attractor-basins", "linoss", "self-evolving", "deterministic"] classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", @@ -25,6 +25,7 @@ classifiers = [ ] dependencies = [ "numpy>=1.26", + "linoss-dynamics>=0.1.0", ] [project.optional-dependencies] diff --git a/src/elume/evolution/__init__.py b/src/elume/evolution/__init__.py index ffa2e90..d95dea9 100644 --- a/src/elume/evolution/__init__.py +++ b/src/elume/evolution/__init__.py @@ -5,7 +5,7 @@ citation and ``README.md`` for the integration-not-invention framing. """ -from elume.evolution.auto_evolver import AutoEvolver +from elume.evolution.auto_evolver import AutoStrategyEvolver from elume.evolution.engine import EvolutionEngine from elume.evolution.operators import ( CrossoverOperator, @@ -17,7 +17,7 @@ from elume.evolution.selection import fitness_tournament, top_k __all__ = [ - "AutoEvolver", + "AutoStrategyEvolver", "CrossoverOperator", "EvolutionEngine", "GenotypeJitterOperator", diff --git a/src/elume/evolution/auto_evolver.py b/src/elume/evolution/auto_evolver.py index b201e79..9494492 100644 --- a/src/elume/evolution/auto_evolver.py +++ b/src/elume/evolution/auto_evolver.py @@ -1,4 +1,4 @@ -"""AutoEvolver — the "just run it and give me better strategies" entry point. +"""AutoStrategyEvolver — the "just run it and give me better strategies" entry point. Wraps an ``EvolutionEngine`` and runs N generations automatically, evaluating fitness at each step and stopping when ``max_generations`` @@ -6,7 +6,7 @@ consecutive generations. Consumers instantiate an ``EvolutionEngine`` with their provider and -operators, seed a population, then hand it to ``AutoEvolver`` with a +operators, seed a population, then hand it to ``AutoStrategyEvolver`` with a fitness function. ``run()`` returns the final population sorted by fitness descending — the best strategy is ``result[0]``. """ @@ -19,7 +19,7 @@ from elume.models.strategy import Strategy -class AutoEvolver: +class AutoStrategyEvolver: """High-level orchestrator that runs N generations of evolution. Args: diff --git a/tests/integration/test_evolution_end_to_end.py b/tests/integration/test_evolution_end_to_end.py index 3c8bbce..00d3083 100644 --- a/tests/integration/test_evolution_end_to_end.py +++ b/tests/integration/test_evolution_end_to_end.py @@ -1,6 +1,6 @@ """Integration test: end-to-end evolution with a toy fitness function. -Seeds a tiny population, runs ``AutoEvolver`` with a fitness function +Seeds a tiny population, runs ``AutoStrategyEvolver`` with a fitness function that rewards ``genotype["temperature"]`` values close to 0.7, and asserts that after N generations: - the best strategy has improved @@ -10,7 +10,7 @@ from __future__ import annotations -from elume.evolution.auto_evolver import AutoEvolver +from elume.evolution.auto_evolver import AutoStrategyEvolver from elume.evolution.engine import EvolutionEngine from elume.evolution.operators import GenotypeJitterOperator from elume.models.strategy import Strategy @@ -40,7 +40,7 @@ def test_fitness_improves_over_generations(self) -> None: seed_best = max(_temperature_fitness(s) for s in seeds) - auto = AutoEvolver( + auto = AutoStrategyEvolver( engine, _temperature_fitness, max_generations=10, @@ -69,7 +69,7 @@ def test_seed_strategies_still_present(self) -> None: ] engine.seed_population(seeds) - auto = AutoEvolver( + auto = AutoStrategyEvolver( engine, _temperature_fitness, max_generations=5, @@ -98,7 +98,7 @@ def test_lineage_dag_intact(self) -> None: ] engine.seed_population(seeds) - auto = AutoEvolver( + auto = AutoStrategyEvolver( engine, _temperature_fitness, max_generations=5, @@ -130,7 +130,7 @@ def test_population_grows_over_generations(self) -> None: ] engine.seed_population(seeds) - auto = AutoEvolver( + auto = AutoStrategyEvolver( engine, _temperature_fitness, max_generations=3, diff --git a/tests/unit/test_auto_evolver.py b/tests/unit/test_auto_evolver.py index 296b126..a60b1e8 100644 --- a/tests/unit/test_auto_evolver.py +++ b/tests/unit/test_auto_evolver.py @@ -1,8 +1,8 @@ -"""Unit tests for AutoEvolver.""" +"""Unit tests for AutoStrategyEvolver.""" from __future__ import annotations -from elume.evolution.auto_evolver import AutoEvolver +from elume.evolution.auto_evolver import AutoStrategyEvolver from elume.evolution.engine import EvolutionEngine from elume.evolution.operators import GenotypeJitterOperator from elume.models.strategy import Strategy @@ -14,7 +14,7 @@ def _make_auto_evolver( seed: int = 42, max_generations: int = 5, stop_on_plateau: int = 3, -) -> tuple[AutoEvolver, InMemoryProvider]: +) -> tuple[AutoStrategyEvolver, InMemoryProvider]: provider = InMemoryProvider() engine = EvolutionEngine( provider, @@ -33,7 +33,7 @@ def _make_auto_evolver( def fitness_fn(s: Strategy) -> float: return -abs(float(s.genotype["temperature"]) - 0.7) - auto = AutoEvolver( + auto = AutoStrategyEvolver( engine, fitness_fn, max_generations=max_generations, @@ -67,7 +67,7 @@ def test_seeds_preserved_in_provider(self) -> None: class TestPlateauDetection: def test_stops_on_plateau(self) -> None: - """When fitness is constant, AutoEvolver should stop early.""" + """When fitness is constant, AutoStrategyEvolver should stop early.""" provider = InMemoryProvider() engine = EvolutionEngine( provider, @@ -86,7 +86,7 @@ def test_stops_on_plateau(self) -> None: def flat_fitness(s: Strategy) -> float: return -abs(float(s.genotype["temperature"]) - 0.7) - auto = AutoEvolver( + auto = AutoStrategyEvolver( engine, flat_fitness, max_generations=100,