Files
Taiga b53dd70a15 [2026-09-26 09:10:28] syncthing: personal/tech/archive-org-collection-harvest.md personal/tech/balda-go-fx-pitfalls.md personal/tech/bilety-oxota-interaktiv.md personal/tech/cloudflare-tunnel-ssh-published-route.md personal/tech/deepseek-language-drift.md personal/tech/docker-data-root-mount-race.md personal/tech/docker-mac-disk-issues.md personal/tech/docker_colima_setup.md personal/tech/eagle-dashboard-launchd.md personal/tech/hermes-agent-improvements.md personal/tech/hermes-browser-toolsets.md personal/tech/hermes-fork-vs-upstream.md personal/tech/hermes-git-repo.md personal/tech/hermes-memory-architecture.md personal/tech/hermes-secret-redaction-bypass.md personal/tech/hermes-self-improvement-review.md personal/tech/home-network-config.md personal/tech/inter-site-throughput-taiga-to-mac.md personal/tech/inter-site-throughput-taiga-to-mac.md.bak-nedstab2 personal/tech/jellyfin-db-schema-mismatch.md personal/tech/karabiner-keyboard-config.md personal/tech/kraken-media-inventory-facts.md personal/tech/kraken-truenas-media-copies.md personal/tech/lampa-jellyfin-plugin.md personal/tech/lampac-deployment.md personal/tech/lampac-torrserver.md personal/tech/llm-model-selection-2026.md personal/tech/mac-print-shared-services.md personal/tech/modbus-rtu-sniffer-debugging.md personal/tech/multistream-single-flow-limit.md personal/tech/multistream-single-flow-limit.md.bak-nedstab2 personal/tech/music-tags-repair-truenas.md personal/tech/obsidian-mcp-ecosystem.md personal/tech/original-xbox-2025-guide.md personal/tech/prowlarr-flaresolverr-cloudflare.md personal/tech/radarr-sonarr-phantom-records.md personal/tech/roundtrip-key-verification.md personal/tech/ryujinx-mac-setup.md personal/tech/skill-arr-media-stack-setup-additions.md personal/tech/skill-docker-data-root-mount-race.md personal/tech/skill-lampac-kraken-deploy.md personal/tech/tirith-trust.md personal/tech/truenas-local-tree-comparison.md personal/tech/truenas-nfs4-acl-and-arrmultiuser.md personal/tech/truenas-zfs-panic-recovery.md personal/tech/vault-doc-pruning.md personal/tech/vless-space-subscription-egress.md personal/tech/watchlist-sync-feed.md personal/tech/watchtower-docker-updates.md personal/tech/xray-outbound-subscription-3xui.md personal/tech/xray-reverse-limits-private-network.md personal/tech/xray-reverse-tunnel-kraken-truenas.md personal/tech/xray-reverse-tunnel-kraken-truenas.md.bak-nedstab2 personal/tech/yandex-music-api.md
2026-09-26 09:10:28 +00:00

10 KiB
Executable File
Raw Permalink 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.

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