Skip to content

fix(forgetting): a source-table label is not a memory kind - #193

Merged
Cipher208 merged 1 commit into
masterfrom
fix/kind-not-source-label
Nov 4, 2026
Merged

Cipher208 merged 1 commit into
masterfrom
fix/kind-not-source-label

Conversation

@Cipher208

@Cipher208 Cipher208 commented Nov 4, 2026 •

Copy link
Copy Markdown
Owner

Что

Два скрипта архивировали core-строки с memory_type="core_memory" — имя таблицы, а не kind. route_of маршрутизирует верно, но _restore_core писал метку прямо в memory_kind, и восстановленная строка получала memory_kind="core_memory" — значение, которого нет в MemoryKind.

Почему это не косметика

decay_importance делает kind_str = r["memory_kind"] or "fact", затем if not validate_kind(kind_str): continue. Значит NULL затухает как fact, а "core_memory" — пропускается. Мусорное значение в kind — не видимая метка, а освобождение от правил.

база архивных строк с меткой живых с memory_kind='core_memory'
Hermes 65 (все speaker_layer_leak, пачка 2026-10-12) 0
Mimocode / CowAgent 0 0

Порча латентная: ни одна ещё не восстановлена.

Kind был в руках и терялся

  • purge_speaker_layer_leaks — его report() даже печатает разбивку по memory_kind;
  • purge_foreign_rows — не было в SELECT, добавлено.

Оба теперь пишут реальный kind, с фолбэком "fact" для пустого.

Restore

_restore_core пишет записанное значение только если validate_kind его принимает, иначе NULL. NULL значит «kind неизвестен»: decay обрабатывает его как fact, а not_recorded говорит об этом по строке через _carried_into_core.

"fact" как подстановку отвергла — выдумывать kind это ровно то, чего route_of избегает. Метку отвергла потому, что она вообще не kind.

Маршрут не менялся: route_of("core_memory") по-прежнему core_memory, иначе 65 старых строк стали бы невосстановимы.

Отменённое решение

Существующий тест утверждал обратное: «видимый мусорный kind лучше выдуманного fact». Это было неверно насчёт цены — мусор в kind не виден как метка, он исключает строку из затухания. Докстринг переписан, цена записана честно: восстановленная строка не выбирается по исходному kind и в decay идёт как fact.

Проверка

  • 2862 passed;
  • ruff чисто, format 756 файлов, mypy 42 = baseline;
  • blind verifier, два круга. Первый: фикс работает end-to-end, но нашёл три дефекта — устаревший докстринг, отсутствие теста на writer purge_foreign_rows, и недостижимую защиту в _carried_into_core, описанную как рабочая. Все три закрыты; второй круг: 6/6 PASS, «Can a source-table label still end up in memory_kind on any path? — NO»;
  • decay проверен живьём: на базе строка пропускалась (decay вернул 0), после фикса затухает (0.9 → 0.667).

Поправка к моим же цифрам

Я говорила, что в core_memory есть невалидные kind bug (5) и relationship (59). relationship — валидный kind. Невалидны только bug (5) и log (2), все на Hermes, все с source='drain' — из restore не пришла ни одна.

Не тронуто

В том же файле пре-существующая неточность: параграф NOTHING IS ERASED утверждает, что каждая удалённая строка копируется в archived_memories, но epi_nodes/epi_edges/epi_tags удаляются без архивации. К этому фиксу отношения не имеет.

Two purge scripts archived core rows with `memory_type="core_memory"` — the
table they came from, not the row's kind. `route_of` routes that correctly, but
`_restore_core` wrote the label straight into `memory_kind`, so a restored row
carried `memory_kind="core_memory"`, which is not a `MemoryKind`.

That is not cosmetic. `decay_importance` does `kind_str = r["memory_kind"] or
"fact"` and then skips any row whose kind does not validate, so such a row never
decays — a junk value is not a visible marker, it is an exemption from the rules.
Measured on the live bases: 65 archived rows carry the label (all on Hermes, one
batch from 2026-10-12) and none has been restored yet, so the damage was latent.

The kind was available at archive time and was being discarded. Both writers now
record it — `purge_speaker_layer_leaks` already had it in its plan,
`purge_foreign_rows` gets `memory_kind` added to its SELECT — with `"fact"` as the
fallback for an empty kind.

On the restore side, `_restore_core` writes the recorded value only when
`validate_kind` accepts it, and `NULL` otherwise. `NULL` means "kind unknown":
decay treats it as `fact` like any row written without a kind, and `not_recorded`
now says so per row via `_carried_into_core`, so a caller learns the kind did not
come back. `"fact"` was rejected as a substitute — inventing a kind is exactly
what `route_of` exists to avoid — and the label was rejected because it is not a
kind at all.

An earlier test argued the opposite: that keeping the label makes the loss
visible. That argument was wrong about the cost, and its docstring is rewritten
to say why. The route itself is unchanged: `route_of("core_memory")` still
returns `core_memory`, because the 65 legacy rows must still be restorable.

Pre-existing invalid kinds already in `core_memory` (`bug` 5 and `log` 2 on
Hermes) are untouched and unrelated: they come from `source='drain'`, not from a
restore. `relationship`, which an earlier note called invalid, is a valid kind.
@coderabbitai

coderabbitai Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@github-actions github-actions Bot added the test label Nov 4, 2026
@Cipher208 Cipher208 changed the title test fix(forgetting): a source-table label is not a memory kind Nov 4, 2026
@Cipher208
Cipher208 marked this pull request as ready for review November 4, 2026 11:20
@Cipher208
Cipher208 merged commit faf7095 into master Nov 4, 2026
19 checks passed
@Cipher208
Cipher208 deleted the fix/kind-not-source-label branch November 4, 2026 11:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant