Learning Memory

GoldenMatch can remember past steward decisions and apply them automatically on every subsequent run. Reject a pair once – it stays rejected. Approve a borderline pair once – it stays approved. After enough corrections accumulate, the learner adjusts matchkey thresholds so the system stops needing the same correction twice.

This is the third layer that sits beside zero-config and explicit YAML: a feedback loop that survives input refresh and re-orders, with no rules to write and no models to train.

Status. Shipped in v1.6.0. Off by default – the zero-config posture is preserved. Enable via config.memory.enabled = True or a memory: block in YAML.


What it does

Learning Memory is a persistent store of (id_a, id_b, decision) corrections plus a learner that turns enough corrections into threshold adjustments.


Quick walkthrough

Three commands. The data and the config don’t change between runs – the system improves because it remembers.

goldenmatch.yml:

matchkeys:
  - name: identity
    type: weighted
    threshold: 0.85
    fields:
      - field: name
        scorer: jaro_winkler
        transforms: [lowercase, strip]
        weight: 1.0
      - field: email
        scorer: exact
        weight: 1.0

blocking:
  strategy: static
  keys:
    - fields: [zip]
      transforms: [lowercase]

memory:
  enabled: true
  backend: sqlite
  path: .goldenmatch/memory.db
  reanchor: true
  dataset: customers
  learning:
    threshold_min_corrections: 10
    weights_min_corrections: 50

Run 1 – produce the review queue. Memory is empty, no corrections apply.

goldenmatch dedupe customers.csv --config goldenmatch.yml

Run 2 – the steward decides. The interactive review TUI writes approve / reject decisions to .goldenmatch/memory.db with source=steward, trust=1.0.

goldenmatch review --config goldenmatch.yml

Run 3 – corrections apply automatically. Same data, same config; the pipeline reads memory, hard-overrides scored pairs, and reports impact in postflight.

goldenmatch dedupe customers.csv --config goldenmatch.yml
# > Memory: 12 corrections applied, 0 stale, 0 stale-ambiguous, 0 unanchorable

After 10+ corrections accumulate against a matchkey, goldenmatch memory learn (or the auto-learn pass on the next pipeline call) tunes that matchkey’s threshold so future runs need fewer corrections.


Configuration

MemoryConfig lives at config.memory. Top-level YAML:

memory:
  enabled: true                 # default: false. Off => no memory work, zero overhead.
  backend: sqlite               # sqlite | postgres
  path: .goldenmatch/memory.db  # sqlite path or postgres DSN
  reanchor: true                # default: true. Set false to require exact (id_a, id_b) match.
  dataset: customers            # tag corrections; isolates per-table memory in shared DBs
  learning:
    threshold_min_corrections: 10   # learner runs once per matchkey at this floor
    weights_min_corrections: 50     # field-weight learning floor (stub in v1.6, returns None)
Field Default Notes
enabled false Zero-config preserved. Enabling does not change pipeline output until corrections exist.
backend "sqlite" "postgres" requires pip install goldenmatch[postgres].
path ".goldenmatch/memory.db" SQLite file or full DSN for postgres.
reanchor true Re-anchor by record_hash when row IDs miss. Disable for strictly positional behavior.
dataset None Use one DB across multiple tables; the pipeline filters corrections by dataset tag.
learning.threshold_min_corrections 10 Trust-weighted grid search runs once a matchkey crosses this floor.
learning.weights_min_corrections 50 Field-weight learning is stubbed in v1.6.0 and returns None.

Postgres backend:

memory:
  enabled: true
  backend: postgres
  path: postgresql://user:pass@host:5432/db
  dataset: customers_prod

CLI

The goldenmatch memory subgroup exposes the store directly.

# Inspect
goldenmatch memory stats --config goldenmatch.yml
goldenmatch memory show --config goldenmatch.yml --limit 50

# Train (run the learner over the current store)
goldenmatch memory learn --config goldenmatch.yml

# Move memory between environments
goldenmatch memory export --config goldenmatch.yml --output corrections.jsonl
goldenmatch memory import --config goldenmatch.yml --input corrections.jsonl
Command Purpose
memory stats Counts by source / decision, learned threshold deltas, last-learned timestamp.
memory show List recent corrections with reason and trust.
memory learn Force a learning pass; otherwise auto-runs at next pipeline call.
memory export JSONL dump of all corrections (one record per line).
memory import Bulk-load corrections from JSONL. Trust-based upsert (higher trust wins).

Python API

import goldenmatch

# Programmatically register a correction (same effect as the review TUI)
goldenmatch.add_correction(
    id_a=42,
    id_b=87,
    decision="reject",
    source="steward",
    reason="Different EIN despite name match",
    dataset="customers",
)

# Force a learning pass (otherwise auto-runs at next pipeline call)
adjustments = goldenmatch.learn()
print(f"Adjusted {len(adjustments)} matchkey thresholds")

# Inspect what's stored
print(goldenmatch.memory_stats())

# Direct store access
store = goldenmatch.get_memory()
for c in store.get_corrections(dataset="customers"):
    print(c.id_a, c.id_b, c.decision, c.trust, c.reason)
Function Returns
goldenmatch.get_memory() The active MemoryStore (constructed from config.memory).
goldenmatch.add_correction(id_a, id_b, decision, ...) Upserts a correction, trust-weighted.
goldenmatch.learn() Runs MemoryLearner, returns the dict of threshold adjustments.
goldenmatch.memory_stats() Same dict the CLI prints.

After a pipeline run, every result also carries a memory_stats field:

result = goldenmatch.dedupe_df(df, config=config)
print(result.memory_stats)
# {'applied': 12, 'stale': 0, 'stale_ambiguous': 0, 'unanchorable': 0}

MCP

Five new MCP tools (memory_*) bring Learning Memory into Claude Desktop / Code. Total tool count is now 35.

Tool Behavior
list_corrections Page through stored corrections, optionally filtered by dataset and source.
add_correction Same arguments as the Python API; writes a correction with caller-supplied trust.
learn_thresholds Runs MemoryLearner.learn(); returns the adjustment dict.
memory_stats Counts and last-learned timestamps.
memory_export Returns all corrections as a JSON array (use server-side for review portability).

A natural-language workflow against an MCP-connected goldenmatch run:

“Show me uncertain pairs from the last goldenmatch run on customers.csv, then mark rows 17 and 23 as not-a-match because they have different EINs.”

The host LLM calls list_corrections -> add_correction -> learn_thresholds.


How it works

            scored_pairs                          stored corrections
                |                                          |
                v                                          v
           apply_corrections() -- match by (id_a,id_b) ----+
                |                                          |
                | row IDs missing?                         |
                v                                          |
            re-anchor via record_hash  <-------------------+
                |
                v
           overridden pairs ---> cluster ---> golden ---> postflight
                                                              |
                                                              v
                                              Memory: N applied, M stale, ...

The full design lives in docs/superpowers/specs/2026-05-04-learning-memory-completion.md for readers who want algorithm-level detail.


When to enable


See also

Topic Link
YAML reference Configuration
goldenmatch memory ... CLI Reference
goldenmatch.add_correction etc. Python API
MCP list_corrections etc. MCP Server
Review queue (the steward UI) REST API