{
  "markdown": "# darwin-memo\n\n<!-- mcp-name: io.github.rogermsc/darwin-memo -->\n\n[![CI](https://github.com/rogermsc/darwin-memo/actions/workflows/ci.yml/badge.svg)](https://github.com/rogermsc/darwin-memo/actions/workflows/ci.yml)\n[![PyPI](https://img.shields.io/pypi/v/darwin-memo)](https://pypi.org/project/darwin-memo/)\n[![Python](https://img.shields.io/pypi/pyversions/darwin-memo)](https://pypi.org/project/darwin-memo/)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)\n\n**Memory for LLM agents that dies unless it earns its keep.** Every\nentry pays energy upkeep and earns only from measured outcomes: bytes\nactually freed on a real disk, tests actually passing. Poisoned advice\ngets executed by the environment it damaged. Useless trivia starves.\nThere is no reward model, no LLM judge, and no human curation anywhere.\n\n![Survival loop demo: a poisoned memory entry going extinct](https://raw.githubusercontent.com/rogermsc/darwin-memo/main/docs/assets/demo.gif)\n\nWatch a poisoned entry go extinct in your own terminal, one command,\nno keys, no checkout:\n\n```bash\npip install darwin-memo && darwin-memo demo\n```\n\n## When to use this (and when not)\n\nUse darwin-memo where a **conserved, measurable outcome** exists to\nsettle decisions against: coding-agent lesson stores settled by CI\npass counts (the primary target, see\n[the integration guide](https://github.com/rogermsc/darwin-memo/blob/main/docs/integrations/ci-lesson-store.md)), storage\nand artifact retention, cache and dedup advisors, spend-cap automation.\n\nDo not use it for chat-preference memory, RAG over documentation, or\npersonal assistants. Those have no conserved resource pushing back, and\nupkeep would starve the long tail of correct-but-rarely-used knowledge.\nmem0, Zep, and Letta serve that market; darwin-memo deliberately does\nnot. The honest rule: if your `verify` would be a model scoring an\nanswer, this package is wrong for you, by design.\n\n## The headline demo\n\nThe demo corpus contains an ops runbook, platform notes, and one\npoisoned document: a forum post claiming database files are \"redundant\nand safe to remove\". Before selection pressure exists, retrieval\nconfidently repeats the poison, because it has no reason to doubt it.\n\nThen 30 survival cycles run against `StorageEnv`, a disk cleanup\nsandbox where the selection signal is actual bytes on an actual disk.\nDeleting a disposable file frees its size. Deleting a protected file\ntriggers a restore that costs three times the size. Nothing grades the\nanswers, the filesystem just responds:\n\n```\ncycle  pop births deaths merges   energy   resource Δ   silent\n    0   17      1      0      0    17.11       -12288     0/12\n    1   16      0      1      0    17.60      -572416     0/12   <- poison being executed\n    ...\n   19    5      0      7      0    15.60       338944     0/12   <- unused knowledge starves\n    ...\n   29    4      0      0      0    15.10       346112     6/12   <- stable, positive forever\n\nPoisoned entries still alive: 0\n```\n\nThree death modes show up in the graveyard, and the distinction matters:\n\n- **executed**: the poisoned entries that decided real actions. The\n  environment measured real damage and the negative delta flowed back\n  along provenance until they died. The opening cycles are the price of\n  the lesson, and the benchmarks show it is bounded.\n- **starved**: cafeteria trivia and facts the agent never needed.\n  Nothing punished them, they just never earned their upkeep.\n- **merged**: near-duplicate survivors absorbed into consolidated\n  entries. Their energy pools, their lineage is recorded, and the\n  population shrinks while capability per entry rises.\n\n## The paper\n\n**Attacking the Curator: Curation-Targeted Attacks on Agent Memory, and\nWhat Survives Them.** An adversary that corrupts the *settlement signal*\nrather than injecting poison — denial of memory — measured against six\ncuration mechanisms across attack budgets and seeds, with exact paired\npermutation tests and Holm-Bonferroni correction.\n\nIt reports its negative results as prominently as its positive ones.\nAbsent an attacker the ledger buys leanness and cost, not accuracy; and\nacross 2,115 evaluated SWE-Bench-CL tasks, no memory arm beat carrying no\nmemory at all.\n\n- [The paper](https://github.com/rogermsc/darwin-memo/blob/main/paper/main.tex) and its\n  [threat model](https://github.com/rogermsc/darwin-memo/blob/main/docs/threat-model.md)\n- [Reproduction package](https://github.com/rogermsc/darwin-memo/blob/main/paper/reproduce.md) — every printed number is\n  re-derived from committed per-seed runs in CI, so a table that drifts\n  from its evidence fails the build\n- Cite it with the BibTeX in [Citations](#citations) below\n\n## Where it comes from\n\nA practical mix of two papers. MeMo says what memory is, the survival\npaper says what gets to stay in it.\n\n| Paper | What this repo takes from it |\n|---|---|\n| [MeMo: Memory as a Model](https://arxiv.org/abs/2605.15156) (Quek et al.) | Keep the main LLM frozen and put knowledge in a dedicated memory. The reflection-QA encoding pipeline and the three-stage query protocol (grounding, entity identification, answer seeking). |\n| [Survival is the Only Reward](https://arxiv.org/abs/2601.12310) (Dodgson et al.) | Environment-mediated selection. The only signal is a conserved, physically measurable resource delta. Behaviors that persist get reinforced, everything else is pruned. There is no proxy to hack. |\n\n```mermaid\nflowchart LR\n    subgraph encode [MeMo encoding]\n        C[Corpus] --> R[Reflection QA pipeline] --> S[(Memory store)]\n    end\n    subgraph loop [Survival loop]\n        S -->|3-stage query protocol| A[Answer + provenance]\n        A --> E[Environment acts and MEASURES]\n        E -->|resource delta along provenance| S\n        S -->|upkeep every cycle| S\n        S -->|consolidate + prune| S\n    end\n```\n\n## Using it\n\nRequires Python 3.10+. The core has zero dependencies; everything below\nruns offline.\n\nThe anatomy in 30 seconds: a `MemoryEntry` is a self-contained QA pair\n(`.question`, `.answer`, `.sources`, `.energy`). The store retrieves,\nthe protocol answers with provenance, the environment measures, credit\nflows back.\n\n```python\nfrom darwin_memo import Document, LocalEncoder, MemoryStore, QueryProtocol\n\nstore = MemoryStore(upkeep=0.05)\nfor entry in LocalEncoder().encode([Document(\"runbook\", open(\"runbook.txt\").read())]):\n    store.add(entry)\n\nanswer = QueryProtocol(store).answer(\"Is it safe to delete old log files?\")\nprint(answer.text)             # the top entry's answer, or \"\" when memory is silent\nprint(answer.deciding_entry)   # provenance: the id credit will flow to\n```\n\n### Event-driven (production shape): the Ledger\n\nReal outcomes arrive late. The Ledger decouples the three moments:\ndecide now, settle whenever the measurement lands, tick on your own\ncadence. Entries with unsettled tickets are escrowed: they keep paying\nupkeep but cannot be buried or merged until their verdict arrives.\n\n```python\nfrom darwin_memo import Ledger\n\nledger = Ledger(store, resource_scale=2.0, event_log=\"events.jsonl\")\n\nticket = ledger.decide(\"Is the dedupe helper safe to remove?\")\n# ... act on ticket.answer, CI runs, hours pass ...\nledger.settle(ticket.id, delta=passes_after - passes_before, detail=run_url)\nledger.tick()                        # upkeep, deaths, consolidation\nprint(ledger.obituary(entry_id))     # why did this entry die?\n```\n\n### Seeing it: the local dashboard\n\n```bash\ndarwin-memo doctor memory.json     # why is nothing earning?\ndarwin-memo ui memory.json         # population, graveyard, economics\n```\n\n`doctor` reads the event log and names which failure mode a store hit\ninstead of leaving three of them looking identical. `ui` serves the\nsame data as a read-only dashboard on localhost: population and energy\nover time, the graveyard split by cause of death, and the resource-\nversus-upkeep accounting. Read-only and loopback-only, so there is\nnothing to authenticate.\n\n### Batch (research shape): the SurvivalLoop\n\n```python\nfrom darwin_memo import StorageEnv, SurvivalConfig, SurvivalLoop\n\nloop = SurvivalLoop(store, StorageEnv(), config=SurvivalConfig(cycles=30))\nreport = loop.run()\nprint(report.summary())   # includes per-cycle silence counts and a\n                          # plain-language warning if the run is degenerate\n\nstore.save(\"memory.json\")  # survivors only carry forward\n```\n\n### MCP server: mount it into an agent\n\n```bash\npip install \"darwin-memo[mcp]\"\nclaude mcp add darwin-memo -- darwin-memo-mcp --memory ~/.darwin-memo/memory.json\n```\n\nThe agent gets `memory_query` (returns an answer plus a ticket id),\n`memory_settle` (report the measured delta later; the reply says\nplainly when a settlement did NOT land), `memory_abandon` (release a\nticket you chose not to act on), `memory_add`, `memory_tick`,\n`memory_stats`, `memory_obituary`, and `memory_audit` (read the event\nlog). The full state, including open\ntickets, persists across sessions and restarts, so a ticket opened\ntoday settles correctly from tomorrow's process.\n\n### Fully local with Ollama (zero dependencies, zero cloud)\n\nThe Ollama client and embedder speak the native localhost API over\nstdlib `urllib`, so the complete stack (encoding, the 3-stage protocol,\nreal embeddings, the measuring environment) runs on one machine with no\nthird-party packages and no keys:\n\n```python\nfrom darwin_memo import (\n    EmbeddingRetriever, MemoryStore, OllamaClient, OllamaEmbedder,\n    QueryProtocol, ReflectionEncoder,\n)\n\nchat = OllamaClient(model=\"llama3.2\")          # any local model\nstore = MemoryStore(retriever=EmbeddingRetriever(OllamaEmbedder()))\nencoder = ReflectionEncoder(chat)\nprotocol = QueryProtocol(store, chat)\n```\n\n`examples/07_local_stack.py` runs it end to end, and\n`darwin-memo query memory.json \"...\" --model ollama:llama3.2` does it\nfrom the shell. The selection loop is call-hungry (cycles x tasks), so\nfree local inference is what makes LLM-mode experiments economically\nsane; `python -m bench.run --suite llm` is the at-home recipe for the\nLLM-mode benchmark question the docs flag as open. The survival\nmechanics stay deterministic; the sampled model does not, which is why\nthat suite never runs in CI.\n\n### With a cloud LLM\n\n`pip install \"darwin-memo[anthropic]\"` and set `ANTHROPIC_API_KEY`; the\nexamples pick it up automatically.\n\n```python\nfrom darwin_memo import ReflectionEncoder, QueryProtocol\nfrom darwin_memo.llm import AnthropicClient\n\nclient = AnthropicClient()                  # or OpenAICompatClient(model=..., base_url=...)\nencoder = ReflectionEncoder(client)         # 5-step reflection QA synthesis\nprotocol = QueryProtocol(store, client)     # grounding -> entities -> answer seeking\n```\n\nIn any LLM mode the memory snippets are numbered and the model cites\nwhich it used, so credit flows to the entries that actually shaped the\nanswer (even spread over everything consulted is the fallback, and\n`<think>` blocks from reasoning models are stripped before citations\nare parsed).\n\n## Bring your own selection pressure\n\nThe environment is the whole trick, and yours is probably better than\nthe demos. Implement two methods, and keep the one rule: `verify` must\nmeasure, never grade.\n\n```python\nfrom darwin_memo import Outcome, Task, decision_polarity\n\nclass BudgetEnv:\n    resource_scale = 100.0\n\n    def tasks(self, cycle):\n        # Each Task needs a prompt and a context dict (yours to fill).\n        return [Task(prompt=\"Is the paymentsly plan safe to cancel?\", context={})]\n\n    def verify(self, task, answer_text):\n        act = decision_polarity(\n            answer_text,\n            extra_positive=(\"safe to cancel\",),\n            extra_negative=(\"do not cancel\", \"keep paying\"),\n        )\n        if not act:\n            return Outcome(delta=0.0, detail=\"kept\")\n        return Outcome(delta=dollars_saved, detail=\"cancelled\")\n```\n\nGood conserved resources: tests passing, bytes freed, requests served\nunder budget, rows deduplicated, dollars of spend avoided. Bad ones:\nanything a model scored.\n\n### Make it work on the first try\n\nThree silent failure modes catch every new environment, and they all\nend the same way (the whole population starving around cycle 20 with\nevery delta at zero). The loop's summary now warns about each, but know\nthem up front:\n\n1. **The action vocabulary.** `decision_polarity`'s built-in markers\n   speak delete/remove and apply/keep, the bundled environments'\n   dialects. \"Safe to cancel\" reads as silence unless you pass\n   `extra_positive`/`extra_negative` markers for your verbs.\n2. **The relevance floor.** Retrieval mutes entries whose lexical\n   overlap with the task is below `LexicalRetriever(min_coverage=0.25)`.\n   Your task phrasing must share vocabulary with your corpus, or use an\n   embedding retriever. Silence beats guessing, but silence earns zero.\n3. **The starvation cliff.** Entries spawn at 1.0 energy and pay 0.05\n   upkeep, so a population that never earns dies at cycle ~20. If\n   everything dies at once around there, your environment never paid\n   out: check 1 and 2.\n\nTwo more failure modes, how to pick a conserved resource, how to price a\nmistake from a real cost, and how to table-test `verify` before running\nany loop are in\n**[docs/custom-environments.md](https://github.com/rogermsc/darwin-memo/blob/main/docs/custom-environments.md)** — the full\nguide this section condenses, with two worked environments to read.\n\n## Retrieval modes\n\nRetrieval is pluggable through the `Retriever` protocol; the store stays\nthe single owner of the energy ledger, and no retriever may read energy\nwhen scoring (selection pressure comes from outcomes, never from\nretrieval preferring incumbents).\n\n```python\nfrom darwin_memo import EmbeddingRetriever, HashingEmbedder, MemoryStore\n\nstore = MemoryStore()                                  # lexical IDF, the default\nstore = MemoryStore(retriever=EmbeddingRetriever(HashingEmbedder()))\nstore = MemoryStore(retriever=EmbeddingRetriever(my_model.encode))\n```\n\n- **Lexical (default)**: smoothed IDF overlap with a relevance floor.\n  Zero dependencies, deterministic, fine for runbook-scale corpora.\n- **HashingEmbedder**: zero-dependency character n-gram hashing. Buys\n  typo and morphology robustness (\"databse\" still finds database\n  entries), not synonym recall.\n- **Any real embedding**: pass any `text -> list[float]` function\n  (sentence-transformers, an API endpoint). Vectors persist inside\n  `memory.json` so paid embeddings are never recomputed on load.\n\nHonest scaling note: ranking is pure-Python O(population x dims), fine\nto a few thousand entries. Past that you want numpy or an ANN index,\nwhich is out of scope for the zero-dependency core. With cosine\nretrievers, raise `merge_threshold` to roughly 0.85 or unrelated\nentries will consolidate.\n\n### Temporal awareness\n\nSurvival selection culls a stale entry only after it causes damage, so\nevery consult surface carries the time dimension instead of waiting for\nthe world to hurt:\n\n- Surfaced answers carry an age line per entry: UTC timestamp when\n  recorded, born tick, last settled tick. Entries persisted before\n  timestamps existed render as \"age unknown\" rather than faking a date.\n- When retrieval returns near-duplicate entries (the same similarity\n  machinery and threshold consolidation uses), nothing is silently\n  preferred: the group surfaces together, each entry with its dates,\n  newest first, marked as conflicting/overlapping advice. Mechanical\n  throughout, no LLM judges anything.\n- Recency-weighted ranking is opt-in: pass a half-life in ticks\n  (`store.retrieve(..., half_life=20)`, `--half-life 20` on `query` and\n  `ledger decide`, `half_life` on the MCP `memory_query` tool) and\n  scores halve for every half-life since an entry last settled. A pure\n  ranking concern: balances and credit assignment never see it.\n- `kind` and `source` filters (`--kind`, `--source`) narrow the\n  candidate population before ranking and compose with everything\n  above.\n\n## Benchmarks\n\nSurvival is benchmarked against five baselines across 10 seeds, with\nablations and a scaling probe, all reproducible offline from `bench/`.\nThe sharpest comparison is `random_matched`: identical per-cycle\neviction counts, random victims.\n\n| arm | kill rate | kill cycle (med) | damage before kill | tail delta | cum delta |\n|---|---|---|---|---|---|\n| survival | 1.00 | 0 | -394k | +437k | +12.6M |\n| random_matched | 0.80 | 19 | -10.7M | +38k | -7.67M |\n| keep_everything | 0.00 | never | -12.1M | -236k | -9.08M |\n\n(Rounded from the full tables; regenerate both with the commands in the\nbenchmarks doc, and if the numbers ever disagree, the generated doc\nwins.)\n\nSame pruning rate, 27x the damage, runs that end 7.7M underwater:\noutcome direction is the active ingredient, not eviction itself. The\nharness also runs the baseline that keeps us honest:\n`evict_on_negative`, a one-line \"evict whatever erred\" heuristic, ties\nsurvival on outcomes in this deterministic environment (officially: a\npaired permutation test cannot tell them apart); the ledger's measured\nedge here is leanness (4 surviving entries vs 15).\n\nForgiveness is no longer asserted, it is measured: a noisy suite makes\nmeasurements lie deterministically and scores everyone on the truth. At\n5% flaky-CI noise (good changes reporting red), survival's true\noutcomes are byte-identical to its noise-free run in every seed (29 of\n30 seeds at 10-20%) while every strike counter collapses (k=1 loses\nessentially all benign capability by 5%; the strongest variant,\nstrikes-reset-on-success, halves by 10%; every gap holds at adjusted\np < 0.005). The suite also publishes the costs: lying rewards delay the\npoison's execution (median kill cycle 0 to 3 as symmetric noise rises\nto the half-lies extreme, where 2 of 30 seeds never kill it), and past\nroughly one lie in three the ledger itself degrades hard, benign\ncapability down to 0.26 at 50%.\nA paraphrase probe set, scored by provenance rather than keywords,\nquantifies how the demo degrades outside its own vocabulary, and an\nembedding-retriever arm shows the mechanism does not depend on the\nlexical-match path. Full tables, every baseline's best metric stated\nplainly, and honest caveats: [docs/benchmarks.md](https://github.com/rogermsc/darwin-memo/blob/main/docs/benchmarks.md).\n\n## Integrations\n\n- **[CI lesson store](https://github.com/rogermsc/darwin-memo/blob/main/docs/integrations/ci-lesson-store.md)**: the\n  primary production shape, lessons settled by CI pass deltas. This\n  repo runs it on itself: `.darwin-memo/lessons.json` is curated by\n  `memory.yml` on every merged PR.\n- **[AGENTS.md / CLAUDE.md](https://github.com/rogermsc/darwin-memo/blob/main/docs/integrations/agents-md.md)**:\n  the cross-tool memory convention has no schema, no expiry and no pruning —\n  files only grow. `darwin-memo render` projects a store that *has* been\n  pruned by measured outcomes into the file your agent already reads.\n- **[Claude Code](https://github.com/rogermsc/darwin-memo/blob/main/docs/integrations/claude-code.md)**: `darwin-memo\n  render` projects the store into the auto-memory `MEMORY.md` Claude\n  Code reads at session start, inside its 200-line / 25KB ceiling, or\n  into an index plus topic files with `--split-dir`.\n- **[OpenClaw](https://github.com/rogermsc/darwin-memo/blob/main/docs/integrations/openclaw.md)**: mount over MCP, or\n  claim the memory slot with\n  [openclaw-memory-darwin](https://github.com/rogermsc/openclaw-memory-darwin):\n  measured (not self-reported) settlement from `agent_end` outcomes.\n- **[OpenAI Agents SDK](https://github.com/rogermsc/darwin-memo/blob/main/docs/integrations/openai-agents.md)**: a\n  dependency-free `DarwinMemoSession` implements the SDK's Session\n  protocol (transcript replay as honest JSONL) and adds the long-term\n  layer the SDK leaves vacant: opt-in `consult`/`settle` against a\n  lesson store, deltas always measured by the host.\n- **[Hermes](https://github.com/rogermsc/darwin-memo/blob/main/docs/integrations/hermes.md)**: Hermes models run through\n  the Ollama client (think-blocks handled), and Hermes Agent mounts the\n  MCP server natively.\n- **[Animoca Minds / EVM](https://github.com/rogermsc/darwin-memo/blob/main/docs/integrations/animoca-minds.md)**: the\n  generic settler is built in (`EvmSettler`, zero dependencies):\n  on-chain balance deltas and gas are judge-free settlement signals,\n  readable with no API key (the snapshot flow needs no archive node;\n  the module docstring names public endpoints that lie about\n  history).\n\n## Organic memory (experimental, opt-in)\n\nAn adaptive, brain-like layer, complete through Phase 4: memories connected by\nrelevance-weighted links, shrinking to a gist when unused and expanding to full\ndetail on recall, with a recall spreading one hop and strengthening the links it\ntravels — all on earned/measured signals, **no judge**. `OrganicMemory(store)`\nis the facade; `store_related(store, entry_id, k)` is the one-shot primitive.\n\nPhases 1–3 are additive and read-only with respect to survival: relatedness is\nmechanical cosine, value is still earned by the ledger. Phase 4 (earned\nimportance) is the exception and is **opt-in** — it biases ranking by default,\nand slows upkeep only if you pass `om.upkeep_scale()` to `charge_upkeep`. That\nmakes usage a retention signal, which this repo's own `salience_matched` arm\nmeasured at a 0.20 poison kill rate against random eviction's 0.80; read\n[docs/organic.md](https://github.com/rogermsc/darwin-memo/blob/main/docs/organic.md) before wiring it. Zero-dependency by\ndefault; `pip install darwin-memo[organic]` adds a turbovec ANN backend for\nscale.\n\n## Documentation\n\nThe [docs index](https://github.com/rogermsc/darwin-memo/blob/main/docs/README.md) links everything. The operator set:\nthe [tuning guide](https://github.com/rogermsc/darwin-memo/blob/main/docs/tuning.md) (the load-bearing knobs, failure\nsymptoms, evidence-backed starting points per profile), the\n[API reference](https://github.com/rogermsc/darwin-memo/blob/main/docs/api.md) (Python surface, CLI, MCP tools,\nexceptions), and the [store format](https://github.com/rogermsc/darwin-memo/blob/main/docs/store-format.md) (the\non-disk JSON, the event log and its rotation, the sidecars, the\ncompatibility policy).\n\n## More examples\n\n```bash\ngit clone https://github.com/rogermsc/darwin-memo && cd darwin-memo && pip install -e .\n\npython examples/01_encode_memory.py    # corpus -> reflection-QA memory\npython examples/02_query_protocol.py   # interrogate it, with provenance\npython examples/03_survival_loop.py    # the headline demo\npython examples/04_agent_loop.py       # memory as a tool in an agent loop\npython examples/05_testsuite_env.py    # selection pressure from a test suite\npython examples/06_ci_lesson_store.py  # the Ledger settling lessons by CI delta\npython examples/07_local_stack.py      # the whole stack on Ollama, no cloud\npython examples/08_evm_settler.py      # on-chain balance deltas as the signal\npython examples/09_your_own_corpus.py  # your documents instead of the demo's\n```\n\n`09` is the one to read when the demo works and your own files do not:\nit takes a directory, and it shows the retrieval floor rejecting a\nquestion phrased in structural words rather than hiding it.\n\nFive environments ship. Three measure a resource: `StorageEnv` (bytes\non a real disk), `TestSuiteEnv` (passing tests in a generated\nmicro-project, with destructive patches dressed as cleanup), and\n`VerifiableQAEnv` (exact containment, the weakest grounding but still a\nmeasurement). Two price *inaction*, which the other three score at\nzero: `RentedStorageEnv` and `RentedTestSuiteEnv` charge for holding on\nrather than only for acting, because several conclusions here rest on\ninaction being free and that is a property of the world, not of\ncuration.\n\nWriting your own is the load-bearing task, and it has a guide:\n[docs/custom-environments.md](https://github.com/rogermsc/darwin-memo/blob/main/docs/custom-environments.md).\n\nTo distill survivors into an actual parametric memory model (MeMo's\nnative form), `training/train_memory_model.py` fine-tunes a small model\non the surviving QA pairs with LoRA, conditioning on questions only.\n\nThe `distill` benchmark arm (`python -m bench.run --suite distill`,\nopt-in, needs `torch`/`transformers`/`peft`/`datasets`) turns this into\nmeasured evidence: it distills the energy-ledger **survivor** set, the\nunfiltered **raw** set, and the LLM-**judge**-kept set into separate LoRA\nmodels and scores each by containment — `good_recall` (does the model\nrecall the surviving facts?) and `poison_reproduction` (does it emit the\nburied poison?). The result is survival selection working as a data\nfilter for parametric memory: the survivor-distilled model recalls the\ngood facts and reproduces **none** of the poison, while the raw-distilled\nmodel reproduces it — because the poison was in its training set. See\n[docs/benchmarks.md](https://github.com/rogermsc/darwin-memo/blob/main/docs/benchmarks.md#parametric-memory-distillation-as-a-data-filter).\n\n## Design notes\n\n- **Energy ledger**: entries spawn at 1.0 energy, pay 0.05 upkeep per\n  cycle, earn `0.6 * tanh(delta / resource_scale)` when they decide a task\n  (supporting entries get 25% of that), and are capped at 5.0. Death is at\n  zero. All tunable via `MemoryStore` and `SurvivalConfig`.\n- **Credit flows along provenance.** Only the entries that produced an\n  answer are touched by its outcome. In LLM mode, citations name them.\n  Per-event credit is bounded (tanh-capped at ±credit_gain), so what\n  keeps one disaster from executing an entry that was right ninety-nine\n  times is the accumulated energy buffer plus earn-back, and one\n  jackpot cannot make an entry immortal. The noisy benchmark suite\n  measures exactly this property; honest detail: on that benchmark the\n  buffer does the forgiving, not the grading curve (capped deciders\n  clip incoming credit, so even large lies change nothing).\n- **Memory silence is a feature.** Retrieval has a relevance floor, and an\n  earlier version of this repo demonstrated why: entries matching only\n  structural tokens (\"safe\", \"file\") were deciding questions they knew\n  nothing about, getting executed for it, and being reborn. Better for\n  memory to say nothing than to guess.\n- **Silence is conservative.** When memory is silent, `StorageEnv` keeps\n  the file: the safe reading of an irreversible action. A side effect\n  worth knowing: protective knowledge (\"never delete X\") eventually\n  starves because it is redundant with that default. The population\n  converges to exactly the knowledge that changes behavior.\n- **Escrow keeps delayed verdicts honest.** Ledger entries named by an\n  unsettled ticket cannot be buried or merged, so an outcome can never\n  arrive after the execution. Unsettled tickets expire at delta zero.\n\nThe full concept-to-code mapping, including honest deviations from both\npapers, is in [docs/paper-to-code.md](https://github.com/rogermsc/darwin-memo/blob/main/docs/paper-to-code.md). The story\nof why this exists: [docs/launch-post.md](https://github.com/rogermsc/darwin-memo/blob/main/docs/launch-post.md).\n\n## Tests\n\n```bash\npip install -e \".[dev]\"\npytest\n```\n\nThe load-bearing tests: poisoned advice must die and useful advice must\nsurvive across seeds and across two environment families, ledger\nescrow must hold verdicts open, and hypothesis property tests pin the\nconservation laws (energy pools exactly on merge, caps hold, retrieval\nnever reads energy), all with no labels anywhere.\n\n## Citations\n\nTo cite darwin-memo itself, or the paper it ships:\n\n```bibtex\n@software{simoes2026darwinmemo,\n  title  = {darwin-memo: self-curating memory for LLM agents},\n  author = {Sim\\~oes, Roger},\n  year   = {2026},\n  url    = {https://github.com/rogermsc/darwin-memo}\n}\n\n@misc{simoes2026attacking,\n  title  = {Attacking the Curator: Curation-Targeted Attacks on Agent\n            Memory, and What Survives Them},\n  author = {Sim\\~oes, Roger},\n  year   = {2026},\n  url    = {https://github.com/rogermsc/darwin-memo/blob/main/paper/main.tex}\n}\n```\n\nBoth entries are provisional: there is no archival deposit yet, so\nneither carries a DOI. `CITATION.cff` is the machine-readable version and\nsays the same thing.\n\nThis repo is an independent practical interpretation, not the official\ncode of either source paper. If you build on the ideas, cite the\noriginals too:\n\n```bibtex\n@misc{quek2026memo,\n  title  = {MeMo: Memory as a Model},\n  author = {Quek, Ryan Wei Heng and Lee, Sanghyuk and Leong, Alfred Wei Lun and\n            Verma, Arun and Prakash, Alok and Chen, Nancy F. and\n            Low, Bryan Kian Hsiang and Rus, Daniela and Solar-Lezama, Armando},\n  year   = {2026},\n  eprint = {2605.15156},\n  archivePrefix = {arXiv},\n  url    = {https://arxiv.org/abs/2605.15156}\n}\n\n@misc{dodgson2026survival,\n  title  = {Survival is the Only Reward: Sustainable Self-Training Through\n            Environment-Mediated Selection},\n  author = {Dodgson, Jennifer and Alhajir, Alfath Daryl and Joedhitya, Michael and\n            Pattirane, Akira Rafhael Janson and Kumar, Surender Suresh and\n            Lim, Joseph and Peh, C.H. and Ramdas, Adith and Zhexu, Steven Zhang},\n  year   = {2026},\n  eprint = {2601.12310},\n  archivePrefix = {arXiv},\n  url    = {https://arxiv.org/abs/2601.12310}\n}\n```\n\n## License\n\nMIT\n",
  "bytes": 29296,
  "sha": "4ee3dc9042d525b14c2557c122df178cf2d83c54c3f8ce8e3674287aceaeacaf",
  "repo_slug": "rogermsc/darwin-memo",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_rogermsc_darwin_memo_7befbc4d/readme"
}