Files
obsidian-vault/personal/tech/vault-doc-pruning.md
T

10 KiB
Raw Blame History

title, created, updated, type, namespace, tags, confidence, status, related
title created updated type namespace tags confidence status related
Чистка устаревших разделов в доках vault — метод 2026-09-15T00:00:00.000Z 2026-09-15T23:59:00.000Z tech personal
obsidian
docs
vault
methodology
pitfalls
unicode
high done
family/how-to/truenas-infrastructure
personal/tech/vless-space-subscription-egress
personal/tech/xray-outbound-subscription-3xui

Чистка устаревших разделов в доках vault — метод

🎯 Когда применять

Док накопил летописи — разделы вида «ПОПЫТКА 1…7», «ПРЕДЫДУЩИЙ ФИНАЛ: ЗАДАЧА НЕ ВЫПОЛНЕНА», «Волна 1 правок». Они описывают состояние, которое уже противоречит факту, и читаются как текущий статус. Alex: «Там никаких летописей не осталось точно?! Только статус и инструкции??»

Целевая форма дока: статус → что хотели → как работает сейчас → процедура → питфоллы → артефакты. Грабли — двумя строками, не разделом.


🔴 Главное правило: вырезать блок ≠ задача выполнена

Ошибка этой сессии: вырезал блок-летопись из 163 строк и отчитался «готово». Через ход Alex спросил про остатки — grep нашёл ещё 6 устаревших мест в том же доке, включая простыню «vless-space НЕ СОЗДАН, БД ОТКАЧЕНА» на строке 101.

Почему так вышло: фокус на одном блоке, ноль проверки остального файла. В доке 990 строк — устаревшие утверждения живут не только в летописях, но и в шапках, таблицах, «Открытых вопросах».

Обязательный шаг после любого вырезания — grep по СМЫСЛУ, а не по имени блока:

grep -rn "НЕ СОЗДАН\|НЕ ВЫПОЛНЕН\|откачен\|НЕ ЗАЛИТ\|следующий шаг\|не начат" \
  --include="*.md" family/ personal/ | grep -v "\.bak"

Плюс пройти по всем связанным докам (в этой задаче их было 5), а не только по тому, где нашёл проблему.


🔴 ПИТФОЛЛ 1: маркер не находится из-за нормализации Unicode (NFC/NFD)

Симптом: скрипт падает на assert end marker not found, хотя строка в файле визуально точно такая, как в коде.

Причина: macOS HFS+/APFS отдаёт имена/строки в NFD, Obsidian и git обычно пишут NFC. Русские буквы с диакритикой (й, ё) и эмодзи (⚠️) в двух формах — это разные байты.

Диагностика — сравнить длину:

awk 'NR==550' file.md | tail -c 200 | cat -v   # покажет \M-^M и прочие артефакты

Если в конце видно \M-^... — строка не в той нормализации, что литерал в скрипте.

Лечение — не искать по точной строке целиком, а матчить по НАЧАЛУ или ХВОСТУ:

# ❌ хрупко: ломается на нормализации и на любом изменении хвоста
END_MARK = "отдельный кусок, не начат."
if END_MARK in ln: ...

# ✅ устойчиво: хвост без диакритики, проверяем через endswith по rstripped строке
END_TAIL = "не начат."
if ln.rstrip().endswith(END_TAIL): ...

Ещё надёжнее — маркер без диакритики/эмодзи (ASCII или простые кириллические буквы без й/ё).

Проверка нормализации файла:

python3 -c "import unicodedata,sys; s=open(sys.argv[1]).read(); print('NFC' if unicodedata.is_normalized('NFC',s) else 'NFD-или-смешанное')" file.md

🔴 ПИТФОЛЛ 2: вырезал блок → украл заголовок секции

Симптом: после вырезания раздела в доке пропал подзаголовок ### xray-admin — 3x-ui панель, и секция стала сиротой в оглавлении.

Причина: заголовок жил внутри вырезанного диапазона (он был частью летописи), а следующий за ним текст — уже живая часть дока.

Ошибка усугубилась: я «восстановил» заголовок вслепую, не проверив, есть ли он ниже. В результате заголовок оказался дважды (строки 333 и 399), и настоящая секция с таблицей параметров шла второй.

Правило: перед вырезанием — grep -n "^#\{1,4\} " file.md и записать, какие заголовки внутри диапазона. После вырезания — проверить дубли:

grep "^### " file.md | sort | uniq -d     # пусто = дублей нет

Проверка стыка глазами — обязательно (читаем 10 строк до и 10 после места реза).


🧱 Процедура (по шагам)

  1. Снять границы. read_file с offset/limit вокруг блока; grep -n "^#" по файлу — полная карта заголовков.
  2. Классифицировать каждый раздел. «Это статус/инструкция» → оставить. «Это рассказ о том, как я ошибался» → вырезать, извлекая грабли.
  3. Извлечь грабли в 1–2 строки перед вырезанием (не после — иначе потеряются).
  4. Написать скрипт-резак в ~/tmp-xray-space/, не sed. Бэкап — первой операцией:
    shutil.copy2(SRC, SRC.with_suffix(".md.bak-before-<что-делаем>"))
    
  5. assert на каждый маркер + на порядок маркеров (start < end). Скрипт обязан упасть, если разметка не та.
  6. Прогнать, проверить стык, grep на остатки + дубли заголовков.
  7. Пройти по связанным докам — тем же grep'ом на смысл.

Почему не sed: прямое указание Alex — «NEVER use in-place scripts or SED for editing». Плюс sed не даёт assert и молча «съест» не тот диапазон.


Чек-лист приёмки

  • Летописей нет: grep -rn "ИСТОРИЯ\|ПОПЫТКА\|ПРЕДЫДУЩИЙ\|Волна" <файлы> → только не относящиеся к теме
  • Дублей заголовков нет: grep "^##\+ " f.md | sort | uniq -d → пусто
  • Стыки читаются (10 строк до/после)
  • Wiki-ссылки живы
  • Бэкап .bak-before-* на месте
  • Связанные доки проверены, не только целевой

⚠️ Проверка wiki-ссылок — только относительным путём. [[obsidian-sync]] — это family/how-to/obsidian-sync.md, не obsidian-sync.md в корне. Проверка «файл существует по литеральному имени» даёт ложные срабатывания:

for n in $(grep -o "\[\[[^]]*\]\]" f.md | sed 's/.*\[\[//;s/\]\]//;s/|.*//'); do
  find . -name "$n.md" -not -path "./.git/*" | head -1
done

Найдено «5 битых ссылок» → все 5 существовали в других папках. Не паниковать до find по всему vault.


📉 Эффект (факт, 2026-09-15)

Файл Было Стало Что убрано
personal/tech/vless-space-subscription-egress.md 854 552 8 разделов-летописей → 2 строки граблей
family/how-to/truenas-infrastructure.md 990 844 блок 163 стр. + чейнджлог моих правок + дубль заголовка

🧱 ГРАБЛИ — 3 пункта (все поймал на себе)

  1. Вырезал один блок — проверь весь файл и все связанные доки. Летописи живут не только в очевидных местах; устаревшее утверждение может сидеть в шапке или в таблице.
  2. Маркер для вырезания — без диакритики и эмодзи, матч по startswith/endswith. Точное совпадение строки ломается на NFC/NFD (macOS отдаёт NFD).
  3. Заголовки внутри вырезаемого диапазона — зафиксировать ДО реза. Восстановив заголовок вслепую, получишь дубль; проверять sort | uniq -d.

Связанные заметки