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

150 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Чистка устаревших разделов в доках vault — метод
created: 2026-09-15T00:00:00.000Z
updated: '2026-09-15T23:59:00.000Z'
type: tech
namespace: personal
tags:
- obsidian
- docs
- vault
- methodology
- pitfalls
- unicode
confidence: high
status: done
related:
- '[[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 по СМЫСЛУ, а не по имени блока:**
```bash
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**. Русские буквы с диакритикой (`й`, `ё`) и эмодзи (`⚠️`) в двух формах — это **разные байты**.
**Диагностика — сравнить длину:**
```bash
awk 'NR==550' file.md | tail -c 200 | cat -v # покажет \M-^M и прочие артефакты
```
Если в конце видно `\M-^...` — строка не в той нормализации, что литерал в скрипте.
**Лечение — не искать по точной строке целиком, а матчить по НАЧАЛУ или ХВОСТУ:**
```python
# ❌ хрупко: ломается на нормализации и на любом изменении хвоста
END_MARK = "отдельный кусок, не начат."
if END_MARK in ln: ...
# ✅ устойчиво: хвост без диакритики, проверяем через endswith по rstripped строке
END_TAIL = "не начат."
if ln.rstrip().endswith(END_TAIL): ...
```
Ещё надёжнее — маркер **без** диакритики/эмодзи (ASCII или простые кириллические буквы без `й`/`ё`).
**Проверка нормализации файла:**
```bash
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` и записать, какие заголовки внутри диапазона. После вырезания — **проверить дубли**:
```bash
grep "^### " file.md | sort | uniq -d # пусто = дублей нет
```
Проверка стыка глазами — обязательно (читаем 10 строк до и 10 после места реза).
---
## 🧱 Процедура (по шагам)
1. **Снять границы.** `read_file` с `offset`/`limit` вокруг блока; `grep -n "^#"` по файлу — полная карта заголовков.
2. **Классифицировать каждый раздел.** «Это статус/инструкция» → оставить. «Это рассказ о том, как я ошибался» → вырезать, **извлекая грабли**.
3. **Извлечь грабли в 1–2 строки** перед вырезанием (не после — иначе потеряются).
4. **Написать скрипт-резак** в `~/tmp-xray-space/`, не sed. Бэкап — первой операцией:
```python
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` в корне. Проверка «файл существует по литеральному имени» даёт **ложные срабатывания**:
> ```bash
> 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`.
## Связанные заметки
- [[family/how-to/truenas-infrastructure]] — где применялся метод (блок `xray-admin`)
- [[personal/tech/vless-space-subscription-egress]] — где применялся метод (летописи попыток)
- [[personal/tech/xray-outbound-subscription-3xui]] — техническая часть той же задачи