---
name: memory-tidy
description: "Привести в порядок всегда-загружаемый индекс памяти MEMORY.md на этой машине (macOS/Linux) — подрезать, унести закрытое в архив, сложить домены в хабы, вернуть выпавшие пины, накрыть сирот указателями. Триггеры “/memory-tidy“, “/mt“, “прибери память“, “прибери индекс“, “почисти MEMORY.md“, “память распухла“, “индекс памяти“, “сироты в памяти“, “tidy memory“"
version: 1.0.0
---

# Memory Tidy — уборка всегда-загружаемого индекса

`MEMORY.md` грузится в КАЖДУЮ сессию. Это не файл, это **аренда, которую платишь на каждом ходу**.
Харнес молча режет хвост за 25 КБ: распухший индекс не «чуть хуже» — он **теряет память без
единого сообщения об ошибке**. Поэтому уборка здесь — не гигиена, а сохранность.

Паспорт детали: `~/.claude/scripts/_doc_memory_tidy.md`. Тело рельсы: `memory_tidy.sh`.

## Когда запускать
* сторож сказал RED (`memory_guard.py`);
* индекс подошёл к мягкой черте (15 КБ / 110 строк);
* руками — когда захотелось порядка;
* ночью само: launchd `ai.paloalto.memory-tidy`, 01:10 и 05:40 по Лиссабону.

## Шаг 0 — ВЛАДЕНИЕ (пропустить = устроить sync-conflict)
```bash
python3 ~/.claude/scripts/memory_tidy_scope.py --explain
```
`~/.claude/projects/` держит три разных вещи, и они выглядят одинаково:
свои индексы · **синкнутые байт-в-байт копии, которыми владеет другой узел** · одноразовые ворктри.
Написать в чужой = два писателя на один файл = потерянная чья-то память. Область объявляется в
`memory_tidy_scope.json`, никогда не угадывается. Незнакомый узел → выход 3 громко, не тишина.

## Шаг 1 — посмотреть, что вообще есть
```bash
sh ~/.claude/scripts/memory_tidy.sh --dry-run --force
```
Печатает вердикт детекторов и рендерит промпт, ничего не меняя. Промпт стоит прочитать глазами —
это то, по чему будет действовать модель.

## Шаг 2 — механическое, без модели (0 токенов)
```bash
python3 ~/.claude/scripts/memory_orphan_cover.py --dry-run   # сироты → указатели в архив
python3 ~/.claude/scripts/memory_orphan_cover.py
```
**Сирота — это не потерянные данные, это недостижимые данные**, что при recall одно и то же:
файл на диске есть, ссылки нет, ни одна сессия его никогда не поднимет. Указатели идут в
`MEMORY-archive.md`, который НЕ грузится, — покрытие стоит 0 живого бюджета.
Хук берётся из `description:` самой заметки, поэтому скрипт не выдумывает: он переписывает.

## Шаг 2-бис — забрать память из сессионных папок (0 токенов, узлы с ворктри)
```bash
python3 ~/.claude/scripts/memory_harvest.py --scan              # только читает
python3 ~/.claude/scripts/memory_harvest.py --harvest --dry-run # что бы записал
python3 ~/.claude/scripts/memory_harvest.py --harvest
```
Заметку, написанную сессией ворктри, кладут в ЕЁ папку проекта, и домашний индекс о ней не знает.
Для домашней сессии такой заметки просто не существует — это та же недостижимость, что и сирота,
только этажом выше. Жатка копирует её домой (оригинал остаётся на месте) и вешает указатель
в `MEMORY-archive.md`.

Выход 3 = **нужен человек, и ничего не потеряно**: тёзка с другими байтами припаркована рядом
как `<слаг>.from-<источник>.md`. Разбираешь руками — слей версии в `<слаг>.md` и удали парковки.
Удаление жатка слышит: парковки назад не приносит, и выход падает до 0.

⛔ На узле без ворктри-папок шаг печатает «records : 0» — это не ошибка, а пустой вход.

## Шаг 3 — свернуть домен в хаб (H2-мандат)
Домен с ≥3 связанными живыми строками и без хаба → строим хаб **в этот же заход**, без спроса:
архитектура утверждена 04.07.2026, применять утверждённое — исполнение, а не новое решение.
```bash
python3 ~/.claude/scripts/memory_fold.py --index <MEMORY.md> --map <mapping.json> --dry-run
```
Суждение (какой слаг в какой домен) — в mapping, его делает модель. Перенос байтов — скрипт.
Так закрыт документированный отказ **summary-grounding**: когда хук переписывают «заодно», и
различающая деталь испаряется. Скрипт не перефразирует — он переносит дословно и проверяет
находимость каждого свёрнутого слага; потеряли находимость → не записано вовсе.

## Шаг 4 — суждение (это делаю я, не скрипт)
* **LIVE** остаётся только standing-правило, которое может выстрелить в сессии, **не открывавшей хаб**.
* **В архив** — закрытые разовые вещи, законченные проекты, отменённые инструменты.
* Перед архивацией — скан хвостов `pending|BLOCKED|TODO|⚠`: недоделка уезжает на доску, не в могилу.
* Сомневаешься, живое ли → **оставь живым**. Пере-подрезать дороже, чем недо-подрезать.
* Обрубленный хук («…перед «X») — это дефект, а не компактность: чини по `description:` заметки.

## Шаг 5 — проверка, и только потом «готово»
```bash
MEMORY_GUARD_EXCLUDE="$(python3 ~/.claude/scripts/memory_tidy_scope.py --exclude)" \
  python3 ~/.claude/scripts/memory_guard.py --force   # обязан GREEN; --force = ночной guard уже
                                                      # отработал сегодня, maintenance_gate режет 2-й
                                                      # заход; в отчёте сказать, зачем форсил
python3 ~/.claude/scripts/memory_focus.py        # обязан 0 missing pins
```
Отчёт модели о собственной работе доказательством не является. Судья — сторож.

## Грабли, оплаченные на этой рельсе
* **Пин чужого узла.** `memory-pin.txt` общий на флот, а слаги в нём хабовые. На другом узле
  тревога «пин выпал» не гасится никогда, а «восстановление» создаёт мёртвую ссылку. Пин относится
  к индексу, только если файл темы лежит рядом с этим индексом.
* **Генерируемый файл в сиротах.** `memory-focus-hints.md` переписывается каждым прогоном детектора;
  считать его сиротой = выдать вечную задачу, которую нельзя закрыть.
* **Красный, который всегда красный**, учит игнорировать красное. Тревога без достижимого зелёного —
  это баг тревоги.
* **Строка-шапка тоже «entry».** Сторож меряет ЛЮБУЮ строку: длинный changelog в
  `<!-- MEMORY.md vX.Y ... -->` в одиночку делает индекс RED (14.09, Маяк).
* **CRLF съедает символ бюджета и подделывает диф.** Сторож срезает только `\n`, поэтому на CRLF-индексе
  каждая строка считается на 1 длиннее (бюджет 149). И читать «универсальными» переводами строк, а
  писать голым `\n` = молча переписать ВЕСЬ файл: для синкаемых копий это фальшивый диф в 80 строк.
* **Разлогиненный headless CLI.** `claude -p` без токена печатал ошибку и выходил НУЛЁМ; auth-гейт
  в `claude_run.py` делает из этого честный 126. Лечится один раз: `claude setup-token`.
* Пути проектов начинаются с `-`, и утилиты читают их как флаги: `md5 -q ./путь`, не `md5 -q путь`.

## Тест
```bash
python3 ~/.claude/scripts/_test_memory_tidy.py   # 11 проверок, ~1 с, без LLM
```
