Skip to content

fix(memory): a maintenance row is audit material, not the owner's speech - #187

Merged
Cipher208 merged 1 commit into
masterfrom
fix/service-rows-are-not-speech
Nov 3, 2026
Merged

Cipher208 merged 1 commit into
masterfrom
fix/service-rows-are-not-speech

Conversation

@Cipher208

Copy link
Copy Markdown
Owner

Что

lifecycle/l0_sweep.py::_journal писал свою сводку без raw_type, поэтому classify_raw отдавал user-message для cleaner_summary: {...}. graph_enrich при чистке мусора делал то же с прозой удалённого узла — а classify_raw якорит только начало строки, так что проза с [ariel recall] в середине тоже читалась как речь.

Дальше строка жила как реплика: graph_miners кластеризовал её, l2_enrich привязывал к сессии, сегментация считала границей разговора, а replay по явному id дистиллировал в L3/L4.

Фикс

Оба служебных писателя ставят SERVICE_RAW_TYPE = "service", и каждый потребитель, который классифицирует строки L0, его учитывает:

место поведение
features/replay.py окно пропускает service
features/replay.py ids отказывает и считает в refused_service, до claim — статус строки не трогается
lifecycle/segment_consolidation.py пропускает service
lifecycle/graph_miners.py, features/l2_enrich.py уже фильтровали по user-message — service отсекается сам
shared/l0.py::sweep_received пропускает: у journal-only строки нет memory-evidence, судить её = ложно ставить UNACCOUNTED_EXIT
lifecycle/l0_tiers.py::close_overdue_received пропускает намеренно — после исключения из sweep_received это единственный closer, накопления нет

ids отказывает видимо, а не молча: processed=0, skipped=0 неотличимо от «уже обработана», и человек продолжал бы повторять id, который не может проиграться никогда. Warning называет счётчик.

Поправка к аудиту, который это инициировал

Порча историческая, не растущая. shared/machine_report.py (коммит 6617282, 29.7 дня назад) уже отказывал cleaner_summary: в дистилляторе, и новейший такой эпизод — 30.2 дня. По данным verifier'а фактическим блокиратором был importance-гейт (все строки после guard ушли в gated_out с importance_gate_bypass), а не machine_report (отказов 0/0/0).

Что оставалось сломанным: классификация — граф, привязка к сессиям и сегментация продолжали читать строку как реплику, — плюс рекапча прозы graph_cleanup, которую текстовый guard не распознаёт.

Проверка

  • 2832 passed (5 новых тестов, все RED→GREEN);
  • ruff check чисто, ruff format 750 файлов, mypy 63 = baseline;
  • blind verifier, два круга. Первый нашёл дыру: replay(ids=…) обходил фильтр и доказал её end-to-end (1 эпизод + 1 узел). Второй круг: 18/18 PASS, дефектов нет; отдельно проверено — смешанный ids, ids только из service, несуществующий id, stale processing, совместимость с вызывающими.

Ответ verifier'а: «Can a service row still become speech on ANY path? — NO».

Остаётся

Существующие строки не тронуты: 21/9/0 эпизодов и 21/9/0 узлов в Hermes/Mimocode/CowAgent. Чистка — отдельное решение владельца.

Остаточный риск: гарантия типовая. Писатель, забывший raw_type, снова получит user-message; на живой базе таких строк сейчас 0.

`lifecycle/l0_sweep.py::_journal` captured its own summary without a raw_type,
so `classify_raw` fell through to `user-message` for `cleaner_summary: {...}`.
`graph_enrich`'s junk re-capture did the same for a deleted node's prose — and
`classify_raw` only anchors the opening, so prose with `[ariel recall]` in the
middle was also read as speech. From there the row was treated as a turn:
`graph_miners` clustered it, `l2_enrich` bound it to a session, segmentation used
it as a conversation boundary, and a named replay distilled it into L3/L4.

Both writers now pass an explicit `SERVICE_RAW_TYPE = "service"`, and every
consumer that classifies L0 rows handles it:

  - replay's window query skips service rows. The distiller already refuses a
    machine report by text, but a structural fact must not depend on a content
    detector — and every nightly pass was claiming the sweep row before
    discarding it;
  - replay's `ids` branch REFUSES a named service row and counts it in
    `refused_service`. Naming an id overrides the gate, not the row's nature:
    the human keeps the ability to wake a real row, and gets a number and a
    warning instead of silence, so a mistyped id from a journal dump is visible
    rather than indistinguishable from "already verdicted". The refusal happens
    before the claim, so the row's status is untouched;
  - `segment_consolidation` skips service rows — a maintenance row is a boundary
    the owner never spoke;
  - `sweep_received` skips them (a journal-only line has no memory evidence to
    find, so judging it would stamp UNACCOUNTED_EXIT for a write that was never
    meant to happen); `close_overdue_received` still closes them by TTL, so they
    cannot accumulate.

Correcting the audit that prompted this: the pollution is HISTORICAL, not
growing. `shared/machine_report.py` (6617282, 29.7 d ago) already refused
`cleaner_summary:` in the distiller, and the newest such episode is 30.2 d old.
What stayed broken is the classification — graph, session binding and
segmentation kept reading the row as a turn — plus the `graph_cleanup` re-capture
of prose that the text guard does not recognise.

Existing rows are untouched: cleaning 21/9/0 episodes and 21/9/0 graph nodes is
an owner decision, not part of this package.
@coderabbitai

coderabbitai Bot commented Nov 3, 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

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.

@github-actions github-actions Bot added the fix label Nov 3, 2026
@Cipher208
Cipher208 merged commit 79e4053 into master Nov 3, 2026
19 checks passed
@Cipher208
Cipher208 deleted the fix/service-rows-are-not-speech branch November 3, 2026 12:39
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