Skip to content

handoff: читатель раньше чистки + хвост живёт в своей session-заметке (v1.2.11 + v1.2.12) - #34

Merged
jojoprison merged 4 commits into
mainfrom
worktree-floating-frolicking-goose
Jul 26, 2026
Merged

handoff: читатель раньше чистки + хвост живёт в своей session-заметке (v1.2.11 + v1.2.12)#34
jojoprison merged 4 commits into
mainfrom
worktree-floating-frolicking-goose

Conversation

@jojoprison

Copy link
Copy Markdown
Owner

Зачем

Meta — Session Handoff дорос до 805 KiB и перестал работать как «тонкий живой индекс», которым объявлен собственным контрактом. Разбор показал, что чинить надо не то, что казалось.

Три замера, которые переопределили решение

  1. Токенный аргумент ложен. Хост-рантайм режет вывод чтения до превью ~2 KB — handoff стоит ≈700 токенов, а не 244k. Зато он физически нечитаем: чтение файла отказывает после 256 KB / 25 000 токенов. Инструкция «при старте прочитать handoff» была неисполнима, а docs/session.md обещал путь чтения, которого в коде не существовало.
  2. Триаж чекбоксов даёт 2,47%, не размер. Резолв ВСЕХ 863 открытых пунктов освободил бы 20 KB из 805: блок остаётся горячим из-за одного открытого - [ ].
  3. 🔑 Хвосты лежали не в том доме. Свежие открытые пункты присутствуют в своих session-заметках лишь в 9% случаев (старые — 34%). Handoff стал единственным домом forward-state — отсюда и рост, и невозможность сжать: схлопывание блоков потеряло бы 257 живых хвостов.

Что сделано

v1.2.11 — читатель, которого не было

  • hot-scan.py собирает открытые хвосты из pending-секций session-заметок; внутри pending-секции голый - буллет тоже хвост (класс v1.1.10 — живое ≠ всегда чекбокс, на живом vault таких 90)
  • инжект дайджеста в SessionStart (hooks.hotDigest): project-scoped, байтовый cap, деградация до нуджа при любом сбое, обязательная строка про протухшую премису
  • handoff-resolver.py + health Step 7.6 — офлайн report-only триаж; инвариант keep-hot импортируется из vault-write.py, а не копируется; свои якоря пункта отделены от унаследованных из заголовка (смешение раздувает «резолвимость» с 27,6% до 60,6%)

v1.2.12 — смена дома хвоста

  • session Step 5 + depth-contract.md: незакрытая нить пишется в свою session-заметку, handoff получает указатель. Строка-указатель ограничена числом сессий; скопированный хвост — ничем
  • handoff-index-upsert в vault-write.py, идемпотентный по ссылке на заметку. Попутно чинит уже сломанный контракт: Step 5 требовал «точную старую секцию из read», но большой handoff читается превью, где целой секции нет ни одной
  • migrate-handoff-to-index.py / restore-handoff-from-bak.py / split-handoff-archive.py — все с --dry-run по умолчанию и .bak до записи

Доказательства, а не обещания

  • dry-run на живом файле: 826 916 B → 35 910 B (−95,7%), 186 строк-указателей
  • откат отрепетирован до миграции: на копии живого файла migrate → restore вернул файл байт-в-байт (md5 совпал)
  • 89 новых тестов, 14 тест-файлов зелёные, lint чистый
  • мутант-килл на четырёх инвариантах: счёт символов вместо байтов, снятый hard cap, игнор плоских буллетов — каждый роняет сюиту

Что осталось за рамками диффа

Миграция самого vault (--apply) — отдельное осознанное действие владельца: vault без версионирования, поэтому команда запускается вручную, после dry-run.

🤖 Generated with Claude Code

jojoprison and others added 4 commits July 25, 2026 19:06
…риаж 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>
@jojoprison
jojoprison merged commit c9a762e into main Jul 26, 2026
2 checks passed
@jojoprison
jojoprison deleted the worktree-floating-frolicking-goose branch July 26, 2026 16:29
jojoprison added a commit that referenced this pull request Jul 26, 2026
… взят из main (1.2.11 слит в 1.2.12), код — из ветки
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant