Skip to content

fix(memory): never resolve an unsourced value conflict at ~explicit - #1832

Open
Chuckos wants to merge 1 commit into
danielmiessler:mainfrom
Chuckos:fix/memory-conflict-rule
Open

fix(memory): never resolve an unsourced value conflict at ~explicit#1832
Chuckos wants to merge 1 commit into
danielmiessler:mainfrom
Chuckos:fix/memory-conflict-rule

Conversation

@Chuckos

@Chuckos Chuckos commented Aug 12, 2026

Copy link
Copy Markdown

Problem

The memory reviewer resolves conflicting factual values by picking a winner and stamping it ~explicit — as if the principal had stated it. When memory already holds ≥2 distinct values for the same topic (a weekday, a time, a quantity) and the reviewer keeps one while evicting a disagreeing sibling, that's the reviewer guessing, not recording something the principal said — yet the guess carries the highest-confidence marker.

This is a confabulation defect: a gap filled by inference, then marked as fact. Consumers that trust ~explicit (and any metric derived from a conflicted value, e.g. a session count) are silently corrupted until someone notices.

Fix

Add a deterministic detectValueConflicts() in MemoryWriter.ts, run in-lock during setEntries:

  • Closed vocabularies only — weekday names, HH:MM, number+unit. Anything outside is left alone by design; a broader rule would have to infer, which is the very defect.
  • Fires only on the true signature — prior state held ≥2 distinct values and a disagreeing sibling was actually evicted. Ordinary supersession (one prior value + a new one) and honest consolidation are untouched.
  • Downgrades, never rejects — a flagged entry's provenance is forced to ~inferred (text preserved). Curation keeps flowing; authority does not.

Two false-positive guards: unit-scoped comparison (7h is not a rival of 150g), and a two-shared-word topic match (so a single shared token doesn't link two entries that are plainly about different things).

Tests

Adds MemoryWriter.conflict.test.ts — 8 cases: the real regression plus false-positive guards (unit scoping, two-word anchor, ordinary supersession, no-closed-vocab, unchanged entries).

The memory reviewer is instructed to SUPERSEDE on contradiction: when a new fact
contradicts an existing entry, drop the old one. That is right when the principal
has just stated the new fact. It is wrong when two stale entries merely disagree
and the reviewer picks a winner -- the pick is a guess, and it is written with an
~explicit tag, which every later read treats as "the principal stated this".

Observed in the wild: a memory file held three incompatible answers to one
question (which days a recurring activity happens). A curation write collapsed
them by choosing one. The choice was wrong, it was recorded at full confidence,
and the correct answer had never been asked for. The same shape had previously
produced a fabricated identity fact that survived several review cycles.

Two halves:

- MemoryReviewer: supersede only on evidence from the conversation. A
  disagreement nothing in the session can settle must be kept, tagged ~inferred,
  and surfaced for confirmation rather than silently decided.

- MemoryWriter: detectValueConflicts(), a pure deterministic check over closed,
  enumerable vocabularies -- weekday names, HH:MM clock values, and number+unit
  quantities, where each unit is its own class. It fires only when the PRIOR
  state already held two or more distinct values for a topic AND a disagreeing
  sibling was evicted, i.e. the reviewer actually resolved an ambiguity. Ordinary
  supersession (one prior value, a new value stated) is untouched. A topic
  requires two shared content words, since one common token links entries that
  are plainly about different things.

Enforcement is proportionate: a detected conflict downgrades the entry's
provenance to ~inferred and records the conflict in the write log. It does not
reject the write, which would freeze curation on a heuristic.

Detection is deliberately narrow. It never asks a model whether two statements
conflict -- that judgement is the defect being removed. Conflicts outside those
vocabularies go undetected by design.

Measured by replaying a real 186 -> 45 curation: 4 flags, 2 true (including the
motivating regression and a genuine review-day conflict that had gone unnoticed),
2 false. The false-positive shape is recorded as a test rather than wished away.
The rule is tuned to over-flag: a false positive costs one provenance downgrade
that the next explicit statement restores, while a false negative leaves a guess
reading as principal-stated indefinitely.

Tests: 8 cases covering the regression, ordinary supersession, cross-unit
non-comparison, and the known false positive.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant