How it works

Reconciliation

Two statements can say the same thing, and storing both is how a memory layer becomes unsearchable inside a week. One rule governs every merge, and it is biased on purpose.

On this page

The problem

A user corrects themselves constantly, and most corrections are restatements: “run migrations against staging”, then “always run migrations against staging”, then “reminder that we run migrations against staging before deploys”. Stored as three rows, the third is the best match for the word migrations and the store now contains two facts per real fact.

That compounds. Duplicate index lines cost budget on every single turn, they crowd out real facts from the same tier, and after a week the retrieval is mostly returning the store’s own history.

So a candidate is compared against what is already held and folded in — under one rule that governs every merge.

The merge rule

A merge must keep every distinctive token of both sides. A replacement missing one is refused, and both entries are kept.

Distinctive tokens are the ones that carry a fact’s identity — identifiers, numbers, codes. Function words and generic nouns (“file”, “name”, “project”) are excluded because they recur in every restatement and hide real differences.

Concretely: “Always run migrations against staging” and “Always run migrations against staging, never production” donot merge. The second says strictly more, and dropping the qualifier is exactly the failure this rule exists to prevent.

Why the test is biased

The test is deliberately biased towards reporting a loss. A false positive — two things reported as distinct that are actually the same — costs one duplicate row, which is recoverable and visible. A false negative — two things merged that were not — deletes a fact permanently, and nothing in the system will ever report it.

Wrong in this directionCostsRecoverable
Reports a duplicate that is notOne extra row, one index line of budget per turnVisible in the library, and deletable
Merges two different factsA fact, permanentlyNo. Nothing reports it.

The asymmetry is the argument. When both errors are invisible, choose the one that leaves evidence.

The ADD / MERGE / REPLACE / REJECT decision

With a model configured, the decision is made once per turn for all candidates together, rather than per candidate. One call sees the whole set, so two statements in the same message that overlap are reconciled against each other rather than against a store neither of them has seen yet.

Any failure falls back to keeping both. That is the same bias as the merge rule, applied one level up: a model that is unavailable, times out, or returns something unparseable must not be able to delete anything.

The response from POST /v1/turns reports what happened to every candidate, so the decision is inspectable rather than inferred from what is in the store afterwards. The API reference has the shape.

Where the result lands

A merged fact inherits the higher of the two tiers, because a fact that was worth promoting did not stop being worth it. New statements that merge into nothing new land in L1, hot, so the very next prompt has them.

What never happens is a merge that demotes a fact as a side effect. A correction that adds a qualifier is a new, stricter fact — and it is stored as one, with the original kept.