10 KiB
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 |
|
high | done |
|
Чистка устаревших разделов в доках 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 после места реза).
🧱 Процедура (по шагам)
- Снять границы.
read_fileсoffset/limitвокруг блока;grep -n "^#"по файлу — полная карта заголовков. - Классифицировать каждый раздел. «Это статус/инструкция» → оставить. «Это рассказ о том, как я ошибался» → вырезать, извлекая грабли.
- Извлечь грабли в 1–2 строки перед вырезанием (не после — иначе потеряются).
- Написать скрипт-резак в
~/tmp-xray-space/, не sed. Бэкап — первой операцией:shutil.copy2(SRC, SRC.with_suffix(".md.bak-before-<что-делаем>")) assertна каждый маркер + на порядок маркеров (start < end). Скрипт обязан упасть, если разметка не та.- Прогнать, проверить стык, grep на остатки + дубли заголовков.
- Пройти по связанным докам — тем же 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 пункта (все поймал на себе)
- Вырезал один блок — проверь весь файл и все связанные доки. Летописи живут не только в очевидных местах; устаревшее утверждение может сидеть в шапке или в таблице.
- Маркер для вырезания — без диакритики и эмодзи, матч по
startswith/endswith. Точное совпадение строки ломается на NFC/NFD (macOS отдаёт NFD). - Заголовки внутри вырезаемого диапазона — зафиксировать ДО реза. Восстановив заголовок вслепую, получишь дубль; проверять
sort | uniq -d.
Связанные заметки
- family/how-to/truenas-infrastructure — где применялся метод (блок
xray-admin) - personal/tech/vless-space-subscription-egress — где применялся метод (летописи попыток)
- personal/tech/xray-outbound-subscription-3xui — техническая часть той же задачи