Imported from ignatenkofi/memshelf-mcp (
adapters/claude-account/skills/shelve/SKILL.md). Install upstream withnpx skills add ignatenkofi/memshelf-mcp --skill shelve. Copyright stays with the author.
shelve — запись эпизода на полку memshelf
Процедура для хостов, где подключён memshelf MCP (инструменты memshelf_*).
Если сервера нет — это не сюда: в репозитории memshelf-mcp лежит prompt-only
фолбэк adapters/claude-code/skills/shelve, он делает то же руками.
Порядок
-
Снимок «до».
memshelf_doctorсderived_stale_after_hours: 6— порог этой полки, а не умолчательные 24 ч. Запомнить число ошибок и предупреждений: итог оценивается сравнением, на полке почти всегда есть застарелые предупреждения, к твоей записи отношения не имеющие. -
Выбрать срез. Шелвится закрытая тема. Активную не шелвить без просьбы. Если пользователь не назвал тему — предложить кандидатов с грубой оценкой веса (chars/4) и получить подтверждение.
-
Проверить дайджест до записи.
memshelf_lint_digestс полемdigest. Контракт: ≤120 слов; названо, что решено, что отклонено и почему, что осталось открытым; референты именованные — никаких голых «мы» и «это»; секретов нет. Отбитый lint дешевле отбитого shelve. -
Записать.
memshelf_shelve:slug— обязательно с префиксом даты:YYYY-MM-DD-краткое-имя-латиницей.kind—topic|session|research.date,span,description,digest,sections.sync— умолчаниеtrue: до записи делается fetch и ff-only на upstream. Грязное отслеживаемое дерево или разошедшаяся ветка отказывают до записи и называют исполнимое лечение; сорванный fetch (офлайн) эпизод не стоит, но уходит вwarnings. Сниматьsyncбез причины не надо.push— умолчаниеfalse.trueпушит коммит шелва и при отказе делает ровно один rebase-повтор. Требуетautocommit(умолчаниеtrue). Решение владельца: без пуша запись не покидает машину.amend: true— если slug уже на полке.retain_until— только для заведомо временной записи.
В ответе прочитать блок
sync:commits_pulled,push_retries,final_sha. Чистый прогон говоритpulled 0, retries 0явно — отсутствие сигнала не выглядит нормой намеренно. -
Доставить эпизод на
origin. Либоpush: trueв шаге 4, либоgit pushруками. Цитировать толькоfinal_shaиз отчёта: локальный sha ребейз перепишет. -
Развилка «бот рендерит / бот стоит» — и решает её прогон doctor ПОСЛЕ пуша. Бот рендерит на
origin/mainи непушнутого эпизода не видит, поэтому до пушаderived-staleверен тривиально: здоровый бот даёт ровно ту же картину, потому что ему пока нечего было рисовать.Порядок: пуш (шаг 5) → дождаться бота (
git fetch origin mainв цикле илиgh run list --workflow=shelf-derived.yml) →memshelf_doctorсderived_stale_after_hours: 6→ и только теперь:- нет находки
derived-stale— бот рендерит, производные не трогать вообще. Ручной коммит производных здесь воссоздаёт конфликт слияния, который разделение «эпизод — источник, остальное — вывод» и убирает; - есть
derived-stale— рендерер стоит:memshelf_rebuild, затем коммит производных по явным путям (см. шаг 7).
Проверять при этом очередь прогонов, а не только последний коммит бота: задание в
queuedпри живом воркфлоу означает отсутствующий раннер, а не мёртвого бота, и ручной rebuild столкнётся с этой очередью, когда ферма вернётся. Сначала снять или дождаться очереди, потом рисовать руками. - нет находки
-
Стейджить по имени файла, никогда
git add -uи неgit add -A.shelveкоммитит сам и только эпизод.rebuildиpurgeменяют рабочее дерево, но коммит не делают.git add -- docs/<category>/<id>.md git diff --cached --name-only # должен быть ровно этот файл git commit -m 'shelve: <id>'Сплошной add сметает производные и то, что оставила параллельная сессия:
recallсlog: trueдописываетrecall-log.tsv, то есть дерево грязнится от чтения памяти, без всякой работы за этим. Замер 19.08 — производный файл попал в историю именно так и не откатывался, потому что между делом легла чужая работа.Коммит производных (только по развилке 6) — тем же способом, вторым коммитом и тоже по путям:
ledger.tsv,INDEX.md,stats.svg,docs/*/.meta.json. -
Снимок «после».
memshelf_doctor— сравнить с шагом 1. Новых предупреждений быть не должно.
Обязательные секции по kind
kind |
кроме Digest |
|---|---|
topic |
Decisions |
session |
Timeline, Open threads |
research |
любая непустая секция |
Имена секций сверяются точно. Нарушение — ошибка до записи
(EpisodeError: kind=session requires section(s) [...]), на полку ничего
не попадает. Порядок рендера известных секций: Decisions → Timeline →
Artifacts → Open threads → Raw excerpts, остальные — после них
в порядке вставки.
Время в Timeline — из date, а не по оценке. У сессии нет часов:
отметки, проставленные по ощущению хода разговора, дрейфуют вперёд. Замер
17.08: поздние отметки оказались на 1 ч 32 мин позже и mtime файла, и
коммита, который они описывали. Дешёвая проверка — date -Iseconds и сверка
с git log --date=iso-local по событиям, которые Timeline упоминает.
Грабли
- Неизвестный ключ — ошибка, а не тихое умолчание. Модели входа закрыты
extra="forbid", опечатка в имени поля отбивается сразу. Так ведут себя сборки от 2026-08-21 и новее; более старая молча проглатывала и ключ, и параметр, которого в ней ещё не было. Если поведение похоже на второе — сверять версию сервера, а не доверять тишине. - Другая полка адресуется
shelf_path, а неshelf. - slug без даты принимается молча — memshelf-mcp#101. Контракт объявлен в описании поля, но не проверяется; недатированное имя ломает сортировку. Проверять глазами перед вызовом.
doctorсразу послеshelveврёт.no-ledger-rowиstale-index— штатное состояние до рендера, на любой ветке включаяmain. Не заводить по ним issue и не чинить руками; развилку решает шаг 6.amendне достаёт архив — memshelf-mcp#117._find_episodeперебирает толькоdocs/<category>, поэтому эпизод вarchive/даётAmendTargetMissing. Правится редактированием файла, не инструментом.amendпо несуществующему слагу — ошибка, а не создание. Обратное: тот же slug в другой категории безamendдаётEpisodeExists— сменитьkindможно только амендом, он же переносит файл.- Производные файлы руками не править. Регенерировать
rebuild-ом и коммитить как есть. index-bloatроллапом НЕ лечится. Бюджет INDEX линеен по размеру полки (INDEX_BASE_TOKENS(200) + INDEX_TOKENS_PER_ENTRY(80) × записей), поэтому превышение может означать только дорогую строку и никогда «много эпизодов».doctorсам называет вышедший за долю термин — заголовок, описание или ссылку, — и действие лежит там же: подрезать иmemshelf_rebuild.- Роллап живёт на своём триггере — доля INDEX в окне
(
INDEX_CONTEXT_SHARE, 3%), и предлагает егоmemshelf_advise, а неdoctor. - Описание капается на 120 символов (
MAX_DESCRIPTION_CHARS) и на записи, и на рендере. Длинноеdescriptionне отвергается, а режется по границе слова с многоточием, и об этом приходит предупреждение. Писать в него навигационную строку, а не второй дайджест: полный рассказ живёт вDigest, который и достаёт recall.
Удаление и замена эпизода
Отдельного delete нет. Если прямого доступа к диску полки нет — только инструментами:
memshelf_shelveтем же slug,amend: true,retain_untilв прошлом; тело — заглушка со ссылкой на действующую запись. Обязательные секции по kind нужны и здесь.memshelf_purgeбез параметров — сухой прогон. Убедиться, что вexpiredровно ожидаемый файл и ничего сверх него.memshelf_purgeсapply: true.memshelf_rebuild, затемmemshelf_doctor, затем коммит по шагу 7 — удаление файла тоже остаётся незакоммиченным.
Шаг 3 — деструктивная операция: без явной санкции владельца не выполнять.
purge убирает файл из рабочего дерева; git-история полки его сохраняет,
и это не стирание.
Границы
- Секреты в дайджест и секции не попадают. Redaction встроена в
shelve, но это страховка, а не разрешение. - Полка приватна. Её содержимое не пересказывать в публичных issues и не выносить в межпроектную шину.
- Сырые транскрипты на полку не копируются: они вход, а не содержимое.