feat(handoff): окно 31 день + указатель всегда с живой целью (v1.2.13) - #36
Merged
Conversation
…риаж handoff (v1.2.11) Continuity получила автоматического ЧИТАТЕЛЯ, которого у неё не было. Замер на живом vault 2026-07-25: handoff 805 KiB, 185 блоков, 863 открытых пункта. Тело втягивалось ради continuity в 6% сессий — потому что прочитать его нечем: Read отказывается на 256 KB / 25000 токенов, а Bash режет вывод до превью. Инструкция «при старте прочитать handoff» была неисполнима, и docs/session.md обещал путь чтения, которого в коде нет. Добавлено: - hot-scan.py — собирает незакрытые хвосты из pending-секций свежих session-заметок; внутри pending-секции голый «- буллет» тоже хвост (класс v1.1.10: живое ≠ всегда чекбокс, на живом vault таких 90); все лимиты в БАЙТАХ (len(str) занижает кириллицу на 34%) - инжект дайджеста в mnemo-context.sh рядом с нуджем: project-scoped (метка проекта = basename git common dir, чтобы воркти отдавал имя проекта, а не своё), байтовый cap, деградация до нуджа при любом сбое, обязательная строка про протухшую премису - handoff-resolver.py + health Step 7.6 — офлайн report-only триаж: потолок выигрыша, классы блоков по payoff, пункты с внешним якорем. Инвариант keep-hot ИМПОРТИРУЕТСЯ из vault-write.py, а не копируется. Свои якоря пункта отделены от унаследованных из заголовка блока: смешение раздувает «резолвимость» с 27.6% до 60.6% Исправлено: - docs/session.md обещал чтение handoff при старте — такого пути не было - «token bomb read every session» как обоснование потолка: реальная цена раздутого handoff — нечитаемость, а не токены - session/SKILL.md теперь честно называет предел гварда: один открытый «- [ ]» пришпиливает блок, поэтому архиватор законно бывает no-op при превышении потолка в 20 раз Тесты: test-hot-scan.py (20), test-context-hook.py (11, CLI obsidian замокан), test-handoff-resolver.py (15). Мутант-килл пройден на двух инвариантах: подмена байтов символами и игнор плоских буллетов роняют сюиту. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…строку-указатель (v1.2.12) Замер, который переопределил реформу: свежие открытые пункты handoff присутствуют в своих session-заметках лишь в 9% случаев (старые — 34%). То есть handoff стал ЕДИНСТВЕННЫМ домом forward-state — отсюда и 805 KiB, и невозможность ни прочитать его, ни сжать. Схлопывать блоки в строки нельзя: потерялись бы 257 живых хвостов. Настоящий фикс — не формат блока, а ДОМ хвоста. - session Step 5 + depth-contract: незакрытая нить пишется в свою session-заметку, handoff получает указатель. Строка-указатель ограничена ЧИСЛОМ СЕССИЙ, скопированный хвост — ничем. - vault-write: action handoff-index-upsert, идемпотентный по ссылке на session-заметку (чекпоинт обновляет свою строку, а не плодит близнеца). Попутно чинит уже сломанный контракт: Step 5 требовал «точную старую секцию из read», но большой handoff читается превью, где целой секции нет. Лимиты в БАЙТАХ; при обрезке жертвуем меткой проекта, но не ссылкой. Блочный путь (archive-handoff) не тронут, его регресс-сюита цела. - migrate-handoff-to-index.py — одноразовая миграция, dry-run по умолчанию. Блоки переносятся ДОСЛОВНО И ЦЕЛИКОМ (не извлечением чекбоксов: 90 плоских буллетов и ~200 строк прозы держат живое без «- [ ]»), session-заметки не трогаются, ничего не удаляется, .bak до записи, проверка дословного присутствия после. Живой замер: 826916 B → 35910 B. - restore-handoff-from-bak.py — откат написан и отрепетирован ДО миграции (vault без git). На копии живого файла: migrate → restore возвращает байт-в-байт, md5 совпал. - split-handoff-archive.py — архив по месяцам + компактный хаб. Холодный файл не безобиден: 717 KiB не открывается (лимит чтения 256 KB), при этом попадает почти в любой поиск. Крупные месяцы дробятся: июнь = 425 KB. 43 новых теста. Мутант-килл: счёт символов вместо байтов и снятый hard cap роняют сюиту. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Поймано живым повторным запуском на уже мигрированном handoff: скрипт не выходил рано при нуле блоков, а честно проходил весь путь и дописывал в хаб архива маркер «сюда перенесены 0 блоков (0 B)» плюс пару пустых строк в конец handoff. Данные не страдали (0 блоков — нечего переносить, бэкапы писались впустую), но повторный прогон обязан быть неотличим от несделанного. Теперь при отсутствии датированных блоков скрипт печатает «handoff уже в индекс-формате — мигрировать нечего» и выходит, не создавая ни бэкапов, ни правок. Два теста пинят это: повторный прогон не меняет ни один из двух файлов и не плодит лишний .bak. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Личные permission-разрешения в публичном плагине выдали бы каждому, кто клонирует репо, заранее одобренный путь к скриптам, которые необратимо переписывают vault. Файл нужен локально, в репозитории ему не место. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…вый откат Аудит пятью независимыми срезами по свежесмерженной реформе. Данные целы (186/186 и 444/444 блоков дословно на месте), но защитные механизмы — нет. Критичное: - ОТКАТ БЫЛ NO-OP. Повторный прогон миграции пишет .bak уже мигрированного файла, а restore выбирал по mtime → «отмена» восстанавливала индекс поверх индекса. Живой vault был именно в этом состоянии. Теперь бэкап обязан выглядеть до-миграционным, иначе требуется явный --stamp. - Повторный split затирал разложенные части (бэкап делался только хабу). - handoff-index-upsert на легаси-файле больше cap выбрасывал собственную новую строку и рапортовал успех — сессия выглядела записанной и не была. - migrate --keep-blocks дублировал указатели при повторе. - Существующие строки не переклипывались, поэтому клейм про границы был ложен на живом файле (754 B при потолке 200). Доки: блок 📮 Handoff в health украл хвост content-lint (тот самый класс из code-discipline); Step 7.6 помечен legacy-only; досвипаны сиблинги контракта; в design-decisions добавлен отсутствовавший раздел про handoff. Живой vault приведён в порядок: битая ссылка И№26, затёкший [[Т-Банк]], дубль указателя, нормализация до 180 строк / 36 459 B. +13 тестов. Все 14 файлов зелёные, lint и release-контракт тоже. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…естный счётчик дайджеста Остаток находок аудита: - clip_to_bytes резал сводку посреди слова без маркера — «BTS: BRIDGE-» читалось как целая запись. Теперь ставится «…», а огрызок короче 12 символов выбрасывается целиком: фрагмент выглядит информацией, не неся её. - Части архива получали date: «2026-07 ч1-01» — не дата, из-за суффикса части. Property-вью по таким файлам молча не отрабатывают. - У частей не было секции Связи — нарушение граф-правила проекта, на неё опираются connect и health. - Дайджест показывал 43 хвоста из 234, не говоря об этом. Теперь пишет «показано N из M» прямо в тексте. 11 живых частей архива приведены в порядок тем же контрактом. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… взят из main (1.2.11 слит в 1.2.12), код — из ветки
…с живой целью (v1.2.13) Ротация индекса стала календарной: keepDays=31 — правило, maxKB=56 — страховка под ним, maxLines удалён. При замеренном темпе (7.5 сессий/день, 234 за 31 день) прежний потолок 180 строк молча отдавал ~24 дня при конфиге, где написано 31. Пять ручек конфига схлопнуты в три. Эвикция безопасна только потому, что закрыты две дыры: relink-orphan-pointers чинит указатели без цели (10 блоков / 170 пунктов на живом волте), backfill-tails-from-archive переносит хвосты, которые жили только в архиве, в их же session-заметку — точечно, 31 пункт из 136 (остальные 105 уже были в заметке другими словами). Скрипты миграции переехали в plugins/mnemo/scripts — у установленного пользователя раньше не было пути миграции вовсе.
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.
Доводка реформы handoff по итогам аудита + ответ на требование j «31 день должен влезать обязательно».
Ротация стала календарной
handoff.keepDays(31) — правило;handoff.maxKB(56) — страховка под ним;maxLinesудалён. Замер живого волта: 7.5 сессий/день, 234 за 31 день — прежний потолок 180 строк молча отдавал ~24 дня при конфиге, где написаноkeepDays: 31. Размер выведен из замера, не из вкуса: 234 × ~195 B ≈ 46 KiB, потолок 56 KiB даёт ~20% запаса и файл по-прежнему читается за один раз. Пять ручек конфига → три.Когда месяц не влезает — файл говорит об этом строкой
> _overflow:. Молча отдавать меньше обещанного — ровно тот дефект, ради которого затевалась реформа.Эвикция безопасна только потому, что закрыты две дыры
relink-orphan-pointers.py— указатель без цели ((без session-заметки)) был косметикой, пока индекс хранил всё; под окном это потеря данных: эвикция убирает последнюю входящую ссылку на блок. Теперь такие указатели ведут в точную часть архива (на живом волте — 10 блоков из 186, 170 открытых пунктов). Миграция их больше не создаёт.backfill-tails-from-archive.py— хвосты, жившие только в холодных частях, переносятся в свою же session-заметку. Узкий пересмотр «не переписываем заметки пользователя»: переносится только реально отсутствующее — 31 пункт из 136, потому что 105 уже были в заметке другими словами. Сравнение нечёткое намеренно (буквальное даёт 0% там, где правда 77%), и оно же делает повторный прогон no-op.Дистрибуция
Скрипты миграции переехали в
plugins/mnemo/scripts/,healthStep 7.6 предлагает их в порядке зависимостей. У установленного пользователя раньше не было пути миграции вовсе: handoff в блочном формате, а новыйsessionпишет указатели.Плюс фиксы аудита (были в этом PR до релиза)
Мёртвый откат (бэкап выбирался по mtime), затирание частей при повторном split, молчаливая потеря указателя на легаси-файле, дубли при
--keep-blocks, отсутствие переклипа существующих строк, украденный хвост в шаблоне отчётаhealth.Тесты: 16 наборов зелёные (+31 новый), lint 7/7,
verify-release.py— v1.2.13.🤖 Generated with Claude Code