The supported memory modes are:
memory=none: no external-memory read or write path.memory=v2: live card authoring plus randomized, contextual Bayesian selection and causal retirement.memory=v2_multitaskwithmemory.writer.authoring_enabled=false: a fixed shared card set with live causal-trial stamps and no new card authoring.
memory=v2 is the production design. It does not use the removed v1
reputation/auction/reconcile/consolidation stack.
One shared LocalMemoryStore owns the durable card bank and its process-local
vector index. The read and write paths use that same store instance.
READ
whole live bank
-> lineage exclusion
-> optional agentic applicability assessment
-> contextual posterior and chance-constrained probability matching
-> randomized offer/control action
-> immutable render + selection lease
-> terminal and delayed-lineage outcomes in the causal ledger
WRITE
strictly valid mutation outcomes
-> author DROP or at most one conditional hypothesis
-> retrieve same-kind neighbors in the configured task or whole-bank scope
-> strict NEW or EQUIVALENT judgment
-> admit new treatment or pool evidence into the exact equivalent
-> author bounded program-strategy exemplars
-> synchronize causal evidence and leases
-> periodic conservative causal retirement
The reader never assigns efficacy from prose, embedding similarity, retrieval,
or the mutator explanation. Those signals generate or describe actions.
The full randomized offer ledger supports offer-policy ITT/OPE. Card usefulness
is learned only from withheld controls and delivered cards explicitly named in
the mutator's grounded card_ids_used output. A delivered-but-uncited card
remains an offer/uptake observation but contributes nothing to reward,
invalidity, delayed-lineage, or retirement inference.
There are two card kinds:
insight: a mutation hypothesis distilled from one parent-to-child outcome.program: a holistic strategy family distilled from a strong concrete program. Equivalent families keep the best representative.
Every authored card contains:
description: one conditional treatment hypothesis;explanation_summary: its proposed mechanism;- task identity and task-description context;
- provenance program ids;
- causal/historical lineage metadata where applicable.
The author is instructed to use the semantic form:
When observable condition C holds, try action A because mechanism M.
Cards are hypotheses, not proven truths. There are no free-form keywords,
content-union merges, or program-source hashes. Program source is retained only
when program_exemplars.store_code=true; the default is metadata plus the
authored strategy.
The write-neighbor query is card_brief(description + explanation_summary).
The default desc_expl embedding scope indexes the same semantic fields.
Embedding proximity only proposes neighbors. The LLM equivalence judge requires
the same intervention and applicability condition for insight cards, or the
same strategy-family applicability condition, load-bearing representation or
state, core procedure, decision logic, update or output policy, and essential
constraints for program cards while ignoring incidental implementation
variants.
The author sees:
- parent and child programs;
- their bounded unified diff;
- the mutator change report and explanation;
- parent fitness, child fitness, and fitness direction;
- direction-normalized signed gain;
- archive disposition.
Invalid children never enter this path: strict metric validity is an extractor
precondition, so validity is not a dead prompt field. The response schema itself
permits only DROP | NEW. DROP has no card; NEW has exactly one card.
Transient or deterministic authoring failures never bank raw mutation notes.
Each mutation record and exemplar is attempted at most
writer.max_ingest_attempts times (default 3) before it is dropped for the
remainder of the run. writer.ingest_call_timeout_s bounds the complete
author/retrieval/equivalence chain for one ingest.
Only after a candidate is authored does the librarian retrieve neighbors. The equivalence response schema permits only:
NEW: no offered neighbor is equivalent under the card-kind rule;EQUIVALENT: one offered id is the same insight intervention or program strategy family.
An equivalent candidate does not rewrite or broaden the banked treatment.
Provenance and founding evidence are pooled into the existing card. In
memory=v2_multitask, this lookup spans the whole bank, so equivalent actions
authored by different tasks share one canonical card. Gain events retain their
task context and randomized use trials retain their task/run labels. A program
representative is replaced using fitness only for same-task equivalents; raw
fitness values never compete across tasks. The write ledger retains the
discarded candidate description so equivalence mistakes can be audited.
Retrieval or equivalence failure currently fails open to NEW; the optional
novelty judge also fails open. This preserves evolution when the memory LLM is
unavailable, but an online dedup miss has no later exhaustive consolidation
pass. Experiments must therefore report residual semantic duplicates.
Causally retired ids and exact normalized authored payloads are tombstoned for the rest of the process. This prevents deterministic retire/re-author loops without a durable compatibility registry. A genuinely different formulation is still eligible for ordinary authoring and causal evaluation.
CausalBanditMemoryProvider considers the whole eligible bank. Agentic RAG, when
enabled, labels semantic applicability over that same universe; it does not own
a separate priority lane.
The policy:
- freezes the pre-treatment evolutionary context and immutable card revisions;
- fits immediate-utility, delayed-lineage-utility, and invalidity heads;
- excludes actions that fail the configured chance constraint;
- samples among the remaining actions from posterior worlds;
- applies explicit proposal exploration;
- randomizes whether the proposed action is actually offered;
- persists proposal/offer/joint propensities and frozen nuisance predictions.
The control action is deliberate randomized evidence, not a retrieval failure. One mutation attempt owns one decision and one terminal. A selection lease protects every offered live card until the corresponding attempt/child is resolved.
At child birth, the system intersects card_ids_used with the immutable
prompt-time slate and freezes the result on the mutation assignment. Missing,
malformed, or hallucinated ids receive no use credit. The terminal carries that
frozen set into the causal ledger; later metadata changes cannot alter it.
Lineage credit is separate from immediate credit. The default depth is 3;
only non-negative archive-accepted best-descendant lift after the configured
same-island opportunity budget trains the delayed head.
The default memory/evictor=causal arm runs during the live writer cadence.
memory/evictor=none is the supported retirement ablation.
A card can be proposed for deletion only when all of the following hold:
- its lineage has at least the configured cited-treatment support;
- the ledger has the configured pooled randomized controls;
- support spans the configured number of assessable discrete
(MAP-Elites island, parent cell)contexts; - no immediate or delayed-lineage outcome is pending;
- both reward heads optimized successfully;
- neither reward head is at a hyperparameter boundary;
- neither reward head has excessive residual-scale boundary mass;
- prediction and deterministic safety integration remain numerically certified;
- under both
UNASSESSEDand optimisticAPPLICABLERAG states, the Wilson Monte Carlo upper bound forP(safe and practically useful)is below the retirement threshold in every supported context.
Selection defines helpfulness relative to zero. Retirement instead uses
practical_effect_quantile: a low quantile (default 0.10) of non-zero
absolute normalized gains in the randomized control arm. Using controls keeps
the practical scale independent of the card effect being judged, and follows
realized task dynamics rather than the problem author's chosen metric bounds.
The quantile is used only after min_global_control measured, non-zero control
magnitudes exist. Exact zeros are intentionally excluded, so this is the scale
of a non-trivial realized step rather than a zero-inflated quantile; with
insufficient support the boundary falls back to zero, preserving harm
retirement while deferring neutral-card retirement.
A context whose remaining feasible positive headroom does not clear the
boundary cannot certify deletion and does not count toward
min_distinct_contexts, but its posterior is still evaluated and any optimistic
keep-vote rescues the card. Sparse support, uncertainty, and numerical-boundary
mass all fail-keep.
The default normalized residual lower bound is 0.01. On tasks whose realized
noise lies below that floor, residual-boundary diagnostics disable retirement
and warn with the startup-validated
memory.posterior_config.reward_residual_sd_bounds knob. This is a loud,
operator-configurable fail-keep envelope; selection and writing continue.
The evictor creates a one-use verdict containing:
- the exact immutable card revision;
- the exact causal evidence version.
CardAdmissionGate immediately consumes that verdict inside the card-store
update, rechecks leases (including historical aliases), and applies the
foreign-task positive-evidence veto before deletion. A changed card or evidence
version rescues the card.
Production causal retirement requires allow_cross_task=false. Cross-task
delivery remains disabled until retirement evidence is identified separately
per source task; the admission veto is only a defensive final check.
With that setting, an empty query or card task_key is warned once and refused
rather than treated as a wildcard.
Consequently, legacy cards with an empty task_key are not candidates. Restamp
them with the correct task key, or set allow_cross_task=true only for a run
whose legacy bank is known to contain one compatible task.
The SQLite evidence ledger and JSON card bank are separate stores, so there is a small residual interval between the final ledger-version read and the bank delete that cannot be made atomic without a shared transaction. The design is fail-closed before that interval and records exact retired ids for audit.
Repeated sweeps are sequential posterior checks, not independent fixed-sample tests. Deterministic evidence-version RNG and the conservative Monte Carlo bound avoid stochastic verdict churn, but model misspecification remains the main sequential-testing risk.
Censored terminals are excluded from reward and safety fitting. This assumes censoring is non-informative conditional on recorded context. A card that causes hangs recorded only as censoring will therefore be retained rather than declared safe or harmful.
Delivered-but-uncited terminals are also excluded from every usefulness head. Cards that stop being cited before reaching minimum use support therefore fail-keep. This is intentional: retirement never fabricates causal support from exposure or staleness.
The canonical graph is config/memory/v2.yaml. Important component groups:
| Group | Values | Purpose |
|---|---|---|
memory/llm |
gemini, qwen_instruct, gpt54_mini |
shared memory LLM router |
memory/applicability |
agentic, none |
semantic RAG assessment or ablation |
memory/context |
global |
task and MAP-Elites decision context |
memory/excluder |
lineage, none |
prevent immediate lineage reuse |
memory/evictor |
causal, none |
causal retirement or explicit ablation |
memory/no_card_evidence |
none |
explicit absence of heuristic no-card evidence |
memory/write |
live, end_of_run, none |
writer cadence |
Use the multitask preset when runs should learn from one populated bank without adding cards:
python run.py problem.name=<task> \
pipeline=memory_guided \
memory=v2_multitask \
memory_bank_dir=/absolute/path/to/shared_memory_bank \
memory.writer.authoring_enabled=falseThis keeps memory/write=live: decisions and terminals continue updating each
run's local posterior, completed selection leases are released, and compact
randomized use_trials are deduplicated onto existing shared cards. The writer
does not build the task-summary, card-author, equivalence, or program-exemplar
agents. v2_multitask already selects memory/evictor=none, so the card ID set
and treatment prose remain fixed.
The mode is not filesystem read-only. cards.json must remain writable for
trial stamps, and selection_leases.json must remain writable for in-flight
coordination. Do not use memory/write=none for this workflow: that removes
the updater as well as authoring, so shared trials are not stamped and
completed-child leases are not promptly released. Keep checkpoint_dir
run-specific and share only memory_bank_dir.
The resolver-safe causal evictor couples its viability probability to
SafetyConstraint.alpha through ${ref:memory.safety::alpha}. A production
composition test instantiates the complete node; merely checking _target_ is
not sufficient.
Reusable bank artifacts live under memory_bank_dir (which defaults to
checkpoint_dir):
| Artifact | Meaning |
|---|---|
cards.json |
authoritative card bank, including compact shared-card usefulness trials |
cards.json.lock |
short cross-process transaction for atomic card persistence |
cards.json.authoring.lock |
cross-process semantic retrieve → judge → admit transaction |
selection_leases.json |
in-flight card reservations for processes sharing the bank |
Parallel runs should share only memory_bank_dir; each run keeps its own
checkpoint_dir (the default Hydra layout already does this). Card persistence,
semantic admission, usefulness-trial updates, and selection leases are guarded
across processes. The vector index remains process-local and refreshes from the
authoritative bank after another process writes.
Run-local artifacts live under checkpoint_dir:
| Artifact | Meaning |
|---|---|
write_ledger.jsonl |
content/equivalence/rejection/retirement audit rows |
memory_v2_selection_evidence.sqlite3 |
immutable decisions, terminals, mutation edges, and lineage outcomes |
memory_events.jsonl |
structured runtime events |
ope_summary.json |
probe-ITT/DR-AIPW policy summary when enough evidence exists |
Relevant outcomes include added, updated, discarded,
rejected_retired, rejected_novelty, rejected_capacity, retired, and
evicted. discarded is the only unledgered benign no-op.
MEMORY_V2_WRITER_SYNC.retired_card_ids is the exact retirement signal.
Assignment/outcome/applicability events and the causal SQLite rows are the
primary source for experiment analysis.
No migration, restamping, or stamping of old ledgers is performed.
At minimum, changes to this subsystem should run:
pytest -q tests/llm tests/memory tests/memory_v2
ruff check .
ruff format --check .
python -m mypy gigaevo/ --ignore-missing-imports --no-error-summarySee MEMORY_LIFECYCLE_TUTORIAL.md for a concrete decision-to-retirement walkthrough and memory_v2_bayesian_system_report.md for the statistical model.