Repository navigation
fix(forgetting): a source-table label is not a memory kind - #193
Merged
Merged
Conversation
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.
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
Cipher208
marked this pull request as ready for review
November 4, 2026 11:20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Что
Два скрипта архивировали 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'speaker_layer_leak, пачка 2026-10-12)Порча латентная: ни одна ещё не восстановлена.
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.Проверка
purge_foreign_rows, и недостижимую защиту в_carried_into_core, описанную как рабочая. Все три закрыты; второй круг: 6/6 PASS, «Can a source-table label still end up inmemory_kindon any path? — NO»;Поправка к моим же цифрам
Я говорила, что в
core_memoryесть невалидные kindbug(5) иrelationship(59).relationship— валидный kind. Невалидны толькоbug(5) иlog(2), все на Hermes, все сsource='drain'— из restore не пришла ни одна.Не тронуто
В том же файле пре-существующая неточность: параграф
NOTHING IS ERASEDутверждает, что каждая удалённая строка копируется вarchived_memories, ноepi_nodes/epi_edges/epi_tagsудаляются без архивации. К этому фиксу отношения не имеет.